Security – Dreaming for the Future 영원한 개발자를 향해서. 월, 13 1월 2025 13:44:09 +0000 ko-KR hourly 1 https://wordpress.org/?v=4.7 108384747 Tech vs. NonTech /index.php/2023/02/12/tech-vs-nontech/ Sun, 12 Feb 2023 09:50:57 +0000 /?p=1037

Continue reading ‘Tech vs. NonTech’ »]]>

조직에서 리더의 역할은 중요하다. 그리고 조직의 규모에 따라 리더의 중요성 역시 비례한다. 대기업의 경우 최상위 리더가 누구냐, 어떤 방향성을 가지느냐가 큰 영향력을 갖는다. 최상위 리더의 방향성을 중간 리더들이 어떻게 해석해서 실행하기 때문이다. 그러나 최상위 리더가 좋은 의도로 방향을 잡아도, 이를 실행하는 중간 리더들의 해석이 잘못되면 좋은 의도가 안좋은(개인적인 생각에 최악인) 결과가 만들어지기도 한다. 더러 이 상황이 내부 조직간의 갈등을 만들어낸다.

최고 기술 리더 – 코드의 품질을 챙기자!

제대로 된 개발자라면 좋은 코드를 작성해야 한다는 것을 이제는 누구나 당연하게 생각한다. 한번 작성한 이후에 절대 처다보지 않을 코드가 아니라면, 결국 이 코드는 내가 계속 유지보수 해야 한다. 물론 내가 아니라도 내 동료가(혹은 누군가는) 이 코드를 맡아서 계속 작업을 이어갈 것이라 더욱 유지보수가 좋은, 고치기 쉬운, 좋은 코드를 작성해야 한다. 좋은 코드는 “품질(Quality)” 높은 코드를 의미한다. 그럼 코드의 품질은 어떻게 바라봐야 할까? 가장 좋은 개발자의 코드 품질은 동료들에 의해 평가되는 것이 가장 좋다고 생각한다. 하지만 이 방법은 지극히 정성적이다. 사람마다 바라보는 시선이 다르기 때문에 코드를 리뷰하는 사람이 누구냐에 따라 호불호가 갈린다.

지속 가능한 코드와 같은 좋은 코딩 문화를 정착시키기 위해서는 이를 계량화시킬 필요가 있다. 다행히 테스트 코드(Test Code)라는 지속 가능한 코드를 작성하기 위해 필수적인 요소가 있고, 이를 계량화시킬 수 있는 나 와 같은 도구들이 있다. 이 도구들을 활용해 테스트가 어느 정도의 메인 코드(Main Code or Business Code)를 커버하는지를 나타내는 테스트 커버리지(Test Coverage)값을 정량적인 품질 지표로 사용할 수 있다.

2010년대 당시 한국의 소프트웨어 개발은 서비스 중심이라기 보다는 SI(System Integration) 중심이었고, 품질을 이야기할 기반조차 없었다. 개발자들 스스로도 좋은 코드보다는 시간에 맞추는 코드에 급급했다. 최고 기술 리더는 적어도 한국을 개발을 선도하는 기업이니만큼 제대로 개발하고, 제대로 서비스가 만들어지는 “문화“를 만들고 싶었다. 정량화는 개발자들이 코드 품질을 객관화하고, 좋은 코드 품질 문화를 쉽게 받아들이고 빠르게 실행할 수 있는 수단이라고 생각했다. 그리나 아직은 코드 품질 개념은 개발자들에게조차 낯선 개념이었다. 보다 확실한 정착이 필요했기에 서비스 배포(Release) 조건에 “테스트 커버리지 80% 이상“이라는 조건을 설정했다.

문제는 평가다

코드 품질이 자연스럽게 평가와 맞물렸다. 테스트 커버리지가 일정 수준을 넘어야 출시할 수 있다는 제도는 서비스 출시의 선결 조건으로 무조건 커버리지가 그 이상이어야 한다는 강제 조항으로 변질되었다. 무슨 일이 있더라도 커버리지는 그 이상을 맞춰야했고, 자연스럽게 개발자의 코딩 역량 역시 코드 커버리지가 좌우하게 됐다.

어느 순간 커버리지의 취지는 없어지고 80%라는 숫자만 남았다. 어찌됐던 서비스는 출시되어야 하고, 출시를 하기 위해서는 80%가 넘어야 했다. 2010년 초반의 자바(Java) 기반 테스트 프레임워크(Framework)는 현재(2023년) 대비 효율성이 높지 않았다. 의미없는 함수 쪼개기와 단위 테스트가 난무하기 시작했다. 품질높은 코드에 대한 고민은 사라지고, 일정을 맞추기 위한 테스트 코드들이 등장했다. 지속 가능한 코드를 위해 쓰여야하는 테스트 코드가 의미없이 생산되어 결국 쓰레기 코드가 되는 어처구니 없는 상황이 만들어졌다.

대기업 조직은 서비스 기획과 개발이 별도의 사일로 형태였다. 몇십 페이지의 기획서가 개발에 전달되고, 개발은 한땀한땀 이를 구현해야 했다. 단위 테스트를 고려하지 않은 코드도 당연히 있기 때문에 예정된 출시일에 80% 기준을 맞추지 못하는 경우도 있다. 결과는 출시일 연기. 코드 품질이라는 이름으로 출시 연기가 당연시되자 기획 조직에서는 이를 곱게 볼리가 없었다.

개발의 논리는 품질을 높여야 서비스 유지보수가 좋다는 것이다. 출시 연기는 당연하다는 논리가 시니어, 주니어를 떠나서 개발 조직 저변에 깔렸다. SI에서 볼 수 없던 일과 생활의 양립(Work and Life Balance)와 같은 그동안 한국에서 볼 수 없던 요소들이 기술 조직에서 문화적 요소로 강조됐다. 출시일을 지키기면서 제대로 된 코드를 작성하기 위한 노력들이 희미해졌다. 문제가 더욱 심각해졌다. 시선에 가시가 돋혔다.

스스로 무너지다.

잦은 서비스 출시 지연과 말도 안되는 서비스 품질에 대한 책임을 지고 최고 기술 책임자는 물러났다. 억지로 테스트 코드를 작성해야했던 개발자들은 환호했고, 제대로 개발할려고 노력했던 개발자들은 퇴사했다. 직전까지 작성하던 테스트는 관리되지 않았고, 억지로 작성했던 테스트 코드들은 삭제되었다. 코드 리뷰에서 테스트 코드가 추가되어 있으면, 테스트 작성할 시간에 서비스 피처를 더 개발하라는 피드백이 돌아왔다. 이후 오랫동안 테스트와 TDD는 금기어가 되었다. 서비스 출시의 주도권은 서비스 조직으로 넘어갔다.

이 상황에서 다음의 의문이 있다.

    • 왜 개발자들은 지속 가능한 코드가 아닌 테스트된 코드를 작성했을까?

    • 왜 개발자들은 서비스 출시일에 출시를 못했을까?

    • 왜 개발자들은 “80%”의 문제에 이의제기하고 개선하지 못했을까?

제대로 된 기술 조직과 리더를 반겼던 개발자들은 스스로 무너졌다. “품질”이라는 이름으로 흥했던 기술 조직이 “품질”로 발목이 잡혔다. 코드 품질을 평가까지 연동시켜 개발자들에게 각인시킬려던 리더의 선한 의지는 “개발 조직”만 생각하는 편협함으로 폄하되었다. 기술 조직(Tech)과 비기술 조직(NonTech)의 대립으로까지 묘사되는 상황이다.

지속 가능한 코드가 아닌 테스트된 코드

커버리지를 높이기 위해 작성하는 테스트는 지속 가능한 좋은 코드를 짜는데 도움이 못된다. 특히 커버리지를 높이기 위해서 아래 그림처럼 코드를 함수로 쪼개고, 분리된 함수에 단위 테스트를 적용하는 방식이 사용되기도 했다. 이런 단위 테스트는 커버리지를 높이지는 몰라도 해당 코드를 통해 가해지는 상태 변경이 이후 코드에 어떤 영향을 미치는지 관리하는데 도움이 안된다.

커버리지 자체를 위한 테스트는 이후 리팩터링(Refactoring)을 진행할 때 거추장스러운 방해물이 된다. 예를 들어 위 그림의 코드에서 만약 if 문을 통채로 들어내면 어떻게 될까? 사려깊은 개발자라면 사라지는 영역에 존재하는 함수가 참조를 되짚어 관련된 코드도 같이 정리해주겠지만, 종종 쓰이지 않는 코드(Dangling Code)와 테스트로만 남겨진다.

물론 이런 류의 테스트를 작성하는 분들도 이 테스트가 본인의 코드 작성 역량을 올리는데 별 도움이 안된다는 것을 안다. 그렇기 때문에 이건 무의미한 작업이다. 점차 테스트에 대한 회의론을 만든다. 실제로 이런 무지성 테스트 작업 때문에 테스트 무용론에 적극 옹오했던 분들도 있었다.

무지성 테스트의 용도는 하나다. 서비스 출시를 위한, 커버리지로 대표되는 품질을 맞춰야했다. 이걸 맞추지 못하면 서비스를 출시할 수 없었다. 서비스를 출시할 수 없다면, 그동안 작업한 내용들에 대한 정당한 평가를 받을 수 없다. 결론적으로 평가다.

커버리지는 정량적으로 테스트가 코드를 어느 정도 담보하는지를 측정한다. 측정은 다시 서비스 출시와 평가로 자연스럽게 이어졌다. 평가 면담에서 98%이상의 커버리지를 본인의 성과라고 이야기하는 개발자들도 있었다. 물론 이정도의 커버리지라면 훌륭하다. 이 노력이 개발자의 코딩 역량을 올려줄 것이라는 추정은 매우 합리적이다. 하지만 제대로 된 코드를 작성하는지는 다분히 정성적(Quality, not Quantity)이다. 정성적인 면을 실제 증명하는 건 서비스의 변화 요청에 얼마나 빠른 대응을 보여줄 수 있느냐이다. 그리고 동료들과의 코드 협업(Pair Programming, Online Code Review)에서 긍정적 영향을 미쳤느냐이다. 코드를 판단하는 건 결국에는 동료 개발자들의 묷이다. 정량적인 커버리지가 정성적인 개발자 역량을 판단한다는 레버로 동작된다는 것이 잘못됐다.

지연된 서비스 출시

서비스는 항상 타이밍이다. 시간이라는 요소가 서비스를 넘어 사업에 미치는 영향은 크다. 따라서 특정 시기를 놓쳐 출시되는 서비스는 최초 단계의 의미를 상실한다. 매출의 큰 부분을 책임지는 사업 담당자라면 서비스의 출시 일정은 필수로 챙겨야 할 핵심 점검 사항이다. 이 시기를 놓치면 서비스 자체가 해도 그만 안해도 그만이 되버리는 경우도 있다. 이건 조직의 규모를 떠나 모두 중요하다.

이 중요성이 제대로 인식되지 못하는 경우가 있다. 사일로화된 조직들이 협업을 하는 경우다. 특히, 각 사일로 조직이 서로 두꺼운 벽을 치고 있다면 중요성을 인식하는게 더 어렵다.

대기업 개발자들은 개발 조직이라는 사일로(Silo)에 갇혀있었다. 품질 높은 개발이 우선이고, 이를 통해 자신은 자신의 사일로 안에서 평가받는다. 다른 사일로에서 결과로 평가를 받는게 아니다. 우리 개발 조직(사일로)의 평가는 프로젝트의 코드 커버리지가 80%를 초과해야 하는 것이다. 어떻게든 달성한 후에 프로젝트가 릴리즈되야 한다. 혹은 릴리즈 할려면 초과해야한다.

대기업은 대기업이다. 돈이 많다. 한두주 정도 출시를 늦춘다고 망하지 않는다. 이정도 지연이면 야근이나 주말 근무를 하지 않아도 된다. 품질 높은 코드와 일과 삶의 균형(Work and Life Balance)를 위해 이정도는 가능하다는 생각을 했을 수도 있다. 대기업이니까.

대기업의 사업 담당자는 답답할 수 밖에는 없다. 서비스 관련 기능 개발은 얼추되는 것 같지만 “코드 품질” 문제로 예정된 출시일을 못 맞춘다고 하니. 그렇다고 이걸 맞추기 위해 개발자들이 야근이라도 해서 이걸 맞추는게 아니라 출시 일정이 뒤로 밀린다. 어느 정도 밀리는 건 감수되는 상황이겠지만, 특정 상황은 앞서 이야기한 것처럼 프로젝트의 의미가 사라진다. 이런 상황이 되풀이되면 “품질 좋은 코드”의 의미에 대한 강한 의구심을 가질 수 밖에 없다.

이의 제기와 개선 도출

프로세스에 이런 문제가 있었음에도 불구하고, 해당 이슈에 대한 개선 방안이 도출되지는 못했다. 분명 이런 문제가 있다는 것을 중간 리더들은 알았을텐데, 왜 이 문제를 리더십에서 해결하지 못했을까? 어딘가에서 막힌 건 분명하다. 막혔기 때문에 결국 최고 기술 리더의 선한 의도가 조직의 실행 담당자(개발자, 엔지니어)들에게는 왜곡되었다.

가장 큰 이유는 심각성에 대한 인식의 차이가 있었다. 사업 조직에서는 일정대로 돌아가지 않는 프로젝트들에 대한 불만이 있었다. 하지만 개발 조직에서는 이를 그 정도로 심각하게 받아들이지 않았다. 개발 조직 구성원에 대한 평가는 결국 개발 조직에서 이뤄지는 것이고, 본인들은 좋은 평가를 받기 위해서는 사일로의 최상단에 있는 리더의 의사 결정을 최대한 실행하는 것이었으리라. 하지만 사일로가 몇개라도 결국은 회사다. 그리고 비즈니스고 매출과 이익이다. 그러나 개발 조직 구성원들은 이 부분 역시 각자가 신경써야만 한다는 것을 제대로 인식하지 못했다. “품질 좋은 코드“를 추구했을 뿐.

이어지는 이슈는 조직의 획일성과 경직성이다. 대기업의 반열에 올라오면서 말그대로 대기업 방식의 상명하복 조직 문화가 제대로 만들어지기 시작했다. 윗선에서 지시한 일에 대해 “이건 아닌 것 같다.”라는 말은 들어보지 못한 것 같다. 이런 경직된 상황에서 현장의 문제를 위로 전달하는 것은 상상하기 힘들다.

기술 조직과 비기술 조직의 충돌

기술 조직과 비기술 조직의 충돌 문제는 대기업에서만 일어나는 일이 아니다. 조직의 규모가 일정 수준 이상이 되면 언제든 발생할 수 있다. 특히 기술 조직이 사일로 형식으로 분리되어 있다면 특히나 더 높은 가능성을 갖는다. 이런 조건은 스타트업(Start-up)이 시리즈 B, C와 같은 조직 확장 단계에 들어갔을 때 형성된다. 이 단계는 기술을 담당하는 C레벨(CTO)을 모시기 마련이고, CTO의 성향에 따라 충돌로 인한 화재가 발생한다.

스타트업에서 서비스 검증을 마쳤다면 본격적으로 이용자를 늘리고, 핵심 서비스를 중심으로 다양한 영역으로 확장을 모색한다. 이를 뒷받침하기 위한 기술 역량을 확보해야한다. 개발 인원이 늘어나는 것은 당연하다. 이때부터 개발은 지속 가능성을 염두에 두고 이뤄져야 한다. 지속 가능성은 당연히 “품질” 높은 코드와 시스템을 요구한다. 이 상황에서 기술에 치우진 리더의 결정이 있다면, 바로 충돌 상황이 만들어진다. 타이밍이 무엇보다도 중요한 스타트업의 입장에서는 기술을 우위에 두는 판단으로 출시 일정이 영향받는다면 심각한 타격을 입는다. 자본력을 갖춘 기업의 입장에서 한두번의 충돌은 값비싼 수업료로 받아들일 수 있지만, 스타트업에게는 바로 생존의 문제로 직결된다. 그렇기 때문에 빠르게 성장할려는 기업의 기술 리더의 책임은 더욱 중요하다. 단순히 기술만 보는 것이 아니라 사업과의 균형추를 지속적으로 맞춰가야 한다.

소는 누가 키우나?

기술의 지향점은 단순히 기술 자체에 머물면 안된다. 조직의 구성원들이 모두 만들고 운영하는 서비스를 이용자에게 제공하는 역할이 “기술”이어야 한다. 서비스를 위한 기술들이 발전되어야 하고, 높은 품질의 코드 역시 이 과정의 한 요소이다.

서비스를 지향하는 기술은 시장의 빠른 검증이 가능하도록 해야한다. 이를 위한 기본 전제는 빠른 서비스 출시다. 고객에게 빠르게 전달하기 위해 무엇에 집중해야하는지, 구성원들이 서로 빠르게 소통할 수 있는 프로세스가 있어야 한다. 기획 측면에서도 고객에게 완전한 것이 아니라 쓸 수 있는 것을 우선해야 한다. 고객에게 제공할려는 가치와 생존 가능한 서비스의 핵심은 무엇인지를 구성원들이 빠르게 소통하고 그 결과물이 구현 과정을 통해 제공되야 한다.

핵심으로 제공된 서비스는 빠르게 개선(Refine)될 수 있어야 한다. 서비스로써의 생존 가능성이 확인됐다면, 그 다음은 이를 빠르게 개선하는 것이다. 빠른 개선이 동작되면서 서비스의 품질을 해칠지 않게 하기 위해서는 테스트 자동화나 배포와 인프라에 대한 자동화등이 뒷받침되야 한다. 개발자가 변화에 능동적인 코드를 만들어야 하고, 이를 서비스 환경에 빠르고 안전하게 배포할 수 있어야 한다. 변화를 두려워해서는 안된다. 고객에게 전달할 새로운 가치로써 이를 반기고 자신있게 진행할 수 있어야 한다.

핵심은 스피드

디지털 전환(Digital Transformation)은 서비스를 제공하는 모든 조직의 핵심이다. 오프라인에 국한된 사업 영역은 이제 없다라고 봐도 무방하다. 이용자 혹은 소비자의 일상이 이미 디지털을 통해 온라인에서 이뤄지고 있기 때문이다. 이와 같은 시장 환경에 대응하기 위해서는 온라인을 통한 서비스 제공 역량을 갖춰야하고, 이용자를 서비스에 붙잡아둬야 한다. 이미 디지탈화된 사용자들은 본인에게 가장 큰 가치를 제공해주는 제공자로 언제든 이동한다. 특히나 연령대가 낮아지면 낮아질수록 서비스 전환에 따른 부담감이 적다. 변화된 시장 환경에 대응하는데 필요한 건 속도(Speed)다.

기술 조직의 지향점은 그렇기 때문에 속도다. 서비스를 제공하는데 있어 이 속도를 보장하고, 기존보다 속도를 높이기 위한 방안을 찾는 것이 기술 조직이 수행해야 할 역할이다. 운영중인 서비스의 품질을 담보한 상태에서 전체 조직의 새로운 시도를 안전하게 제공해야 할 역할을 기술 조직이 담당하기 때문이다. 때문에 품질이라는 요소는 속도라는 지향점을 향하게 하는 구성 요소라고 생각된다.

TL/리더가 중요하다

이 상황에서 가장 중요한 역할은 기술 리더(Tech Lead)다. 기술의 지향점을 명확하게 구성원들에게 전달하고, 그 방향에서 “우리”는 회사가 지향하는 고객 가치를 어떻게, 어느 시점에 전달할지 합일점을 만들어내는 역할이기 때문이다. 그리고 이 지점이 기술로 만들어질 수 있도록 선두에서 리드해야 한다. 때문에 기술과 리더십을 필요하다. 특히나 리더십의 경우는 배운다고 배울 수는 없다. 리딩(Leading)은 조직에 대한 헌신(Commitment)과 내려놓음의 끝판왕이다.

상황에 따라 리더의 역할은 달라진다. 때문에 우리의 북극성은 어디에 있는지, 나아가고 있는 방향이 맞는지 상위 리더십과 지속적으로 소통해야 한다. 그래야지 상황을 명확하게 구성원들에게 공유하고, 무엇을 실행할지를 결정할 수 있다. 특히나 기술 조직의 리더라면 서비스를 실행하고, 기술 혁신을 통해 구성원들이 달성할 수 있도록 조율자가 되야한다. 그래야만 기술 조직의 성과가 기술 조직을 넘어 전체의 속도를 가속화할 수 있는 촉매제가 될 수 있기 때문이다.

TL/리더분들의 화이팅을 기원한다.

– 끝 –

]]> 1037
팀장님은 어느 팀 소속인가요? /index.php/2022/11/12/what-team-a-leader-belongs-to/ Sat, 12 Nov 2022 09:58:27 +0000 /?p=962

Continue reading ‘팀장님은 어느 팀 소속인가요?’ »]]> 팀장의 팀은 어느 팀인가?

어처구니 없는 질문 같지만 팀장 A를 생각해보자. A 역시 상사인 그룹장이 책임지는 팀들 가운데 한 팀장이다. A씨에게서 팀장이라는 타이틀을 빼고 본 자연인 A만 보자. 그럼 A가 속한 팀은 어느 팀일까? 이 상황이 되고보면 처음 질문이 그리 어처구니 없는 질문은 아니다.

A는 본인이 책임지는 팀(팀원들)이 있다. 하지만 역시 그룹장(상위 조직장)에게 그는 팀원이다.
팀장으로써 있던 팀일까? 아니면 그룹장의 팀원일까?

조직의 가장 말단에 있는 사람에게는 이런 애매한 상황이 발생하지 않는다. 구성원이 리더가 되면 필연적으로 이 상황을 맞닥들인다.
대부분 본인이 책임지는 팀이 자신이 속한 팀이라고 믿는다. 그룹장은 자신의 상사이다. 그러나 상사에게는 A씨 이외에도 B, C, D와 같은 그룹에 있는 팀들의 팀장들이 있다. 그럼 A에게 B, C, D는 어떤 존재일까?

예전 대기업 다니던 친구의 이야기를 들어보면 무시무시했다. 다른 팀장은 무조건 꺽어야 한다. 그래야 내가 부장을 달고, 이사를 달 수 있다. 어린 시절의 친구, 선후배는 치기어린 감정이다. 직속 상사를 보고, 직속 상사의 상사를 보는 라인을 타야한다… 이 상황의 A에게 B, C, D는 “경쟁 상대“일 뿐이다. 설령 함께하더라도 진심이 없는 껍데기뿐이다. A에게 가장 중요한 건 남이 아닌 본인의 성공이다. 우리(조직)가 아니라 나(개인)만이 존재한다. 결국 조직에 수많은 “나”들이 모여있는 것 뿐이다. 이 와중에 뛰어난 A가 많다면 조직이 생존(성공)할 것이고, 아님 망하는 거겠지.

왜 이렇게까지 하는걸까? 경쟁의 시대에서 오로지 믿을 건 “나”와 “나의 능력”뿐이라고 생각하기 때문이겠지.

경쟁이 아니라 협력이다.

회사는 달성하고자하는 목적(숭고하든 걍 돈이든)을 가지고 있다. 열심히 노력해서 입사해서 일을 한다는 건 그 목적에 함께한다는 것을 의미한다. 동료 역시 그 목적을 달성하기 위해 노력하는 사람이다. 그렇다면 그 공동의 목표를 더욱 빨리 달성할려면 어떻게? 당연히 서로 도와야 한다. 혼자 가는 길이라면 2박 3일 시간 걸릴 것이 둘이 가면 1박 2일, 서넛이 간다면 하루 안에도 그 목표에 도착할 수 있다. 그것이 함께하는 동료의 힘이 아닐까?

회사 일을 하는 A, B, C, D는 각자가 회사에서 의미있는 자리를 담당한다. 비슷한 일을 할 수도 있고, 아예 다른 일을 할 수도 있다. 비슷한 일을 한다면 높은 품질을 낼 수 있는 노하우를 공유함으로써 서로의 발전을 도울 수 있다. 다른 일을 한다면 각자 일의 부족한 부분을 전문가적인 역량으로 메워주고 채워줄 수 있다. 발전과 성장을 통해 조직은 튼튼해지고, 개인이 달성할 수 없는 더 큰 목표를 이룰 수 있다.

이런 모습을 A, B, C, D가 함께 보여준다면 이들을 경쟁자가 아닌 동료라고 부른다. 또 이들이 같은 직속 상사 아래 있으면, 우린 이들을 “팀”이라고 칭한다.

다시 원래의 질문으로 돌아가보자. “팀장”이란 역할을 가진 A는 2개의 팀에 속한다. 한 팀은 A가 리딩하는 팀이다. 또 다른 팀은 B, C, D와 함께하는 팀이다. 그 팀안에서 “팀원“으로써 그룹장의 리딩을 받는다.

개인이 속한 2개 팀 가운데 어느 팀이 더 중한지 이야기한다면? 조직의 미션/비전을 “실행“하는 관점에서 생각하면, A는 팀원으로 존재하는 “함께하는 팀“에 더 우선 순위를 둬야한다고 본다. 함께하는 과정을 통해 함께 결과를 만들기 위해 노력하는 것이다. 그리고 그 연장선에서 “리딩하는 팀“에 조직의 방향과 부합하는 방향을 제시할 수 있어야 한다.

협력과 협업을 ““으로써 수행할 때 우리는 더 높은 더 먼 미션을 달성할 수 있기 때문이다. 우리가 오늘만 사는 것이 아니라면 꿈꾸는 것을 이루기 위한 여정은 계속된다. 그리고 그 여정에서 의미있는 목표점을 회사라는 조직, 그리고 구성원들과 협업을 통해 만들어내는 성취 역시 인생이라는 여정을 통한 최고의 경험일 것이다.

화이팅!

– 끝 –

]]> 962
Spring-security를 활용한 JWToken 인증하기 /index.php/2020/08/31/web-bearer-authorization-with-jwt-and-spring-security/ Mon, 31 Aug 2020 01:44:40 +0000 /?p=782

Continue reading ‘Spring-security를 활용한 JWToken 인증하기’ »]]> 퍼블릭 환경에서 제공되는 API 서비스를 만들 때 가장 고민되는 부분은 보안이다. API가 제공하는 기능이 민감한 정보가 아니라면 개발자 입장에서 행복하다. 하지만 값어치가 나가는 유료 정보나 개인 정보 혹은 개인화 기반 정보가 제공되는 경우에는 고민이 깊어진다.

General Web Security

보안 정책을 웹 환경에서 구현하는 방법은 여러가지가 있을 수 있다. 보호 대상의 성격에 따라 다를 수 있고, 서비스 혹은 시스템의 연계성에 의해서 방법이 변경될 수 있다. 그럼에도 큰 맥락에서 2가지 기능으로 이 보안 정책은 구현된다.

Authentication

인증은 말 그대로 접근할려는 사용자 혹은 시스템이 맞는지 확인하는 절차다. 일반적으로 ID/Password를 이용한다. 요구되는 보안 사항이 높지 않은 경우, 이것만으로도 충분하다. 물론 이것보다 간단하게 생각되는 방식이 API Key 방식이다. 하지만 ID/Password나 API Key나 따지고 보면 같다. 개발자 관점에서 값을 하나를 사용할지 두개를 쓸지 차이뿐.

인증은 서비스에 접근한 존재가 누군지 알 수 있려준다. 여기에서 좀 더 복잡한 그 다음 프로세스를 원한다면 인증이 성공했다를 알려준다. 이 알려준 값은 권한(Authorization) 체크 용도로 활용될 수 있다. System vs. System 사이의 연동이라면 인증 처리만으로도 충분히 권한까지 함께 처리할 수 있다. 만약 사용자에 관련된 이슈라면?

Authorization

권한은 간단히 이야기하면, 인증을 요구하는 요청이 적합한지, 실행 권한이 적절히 부여되어 있는지 확인하는 절차다. Authorization이란 단어를 생각하면 “권한”을 먼저 생각하겠지… 하지만 현실에서는 올바르게 인증된 경우를 체크하는게 일반사다. 실제로 권한을 체크할려면, 권한 관리 시스템(모듈)을 통해 권한 클라이언트가 필요로하는 권한을 가지고 있는지 요청해야 한다. 권한 여부에 따라 True/False 결과만 확인하면 된다. 얼핏 이야기했지만 상당히 복잡하다. 복잡하다는 건 확인을 위한 Operation Cost가 많이 든다는 걸 의미한다. 그래서 이와 같은 권한 체크는 일반 사용자보다는 어드민 혹은 관리 시스템들에 한해 적용한다. 관리 시스템은 통상 내부망에서 동작하기 때문에 이런 복잡성을 굳이 가질 필요가 없다. 쉬운 방법을 충분히 찾을 수 있다.

결론적으로 일반 사용자, 그리고 대용량 사용자 대상 서비스의 경우에는 요청이 올바른 인증 정보를 포함하고 있는지만 확인하는 것만으로도 충분하다.

OAuth and JWT – JSON Web Token

이와 같은 인증 및 권한 관리 체계의 대표적인 모델이 OAuth 모델이다. 이를 구현하기 위해 적용된 기본 기술이 JWT이다. 예전에 JWT가 뭔지 어떻게 활용하면 되는지 정리한 글이 있으니, 더 궁금한 분이 있다면 참고한다. OAuth에 대해 안다고 주접떨기 보다는 아래 2개 링크를 참고하자.

  • https://ko.wikipedia.org/wiki/OAuth – OAuth wikipedia, 기본 개념정도는 이해 가능.
  • https://oauth.net/2/ – OAuth 2.0 standard

간단히 동작되는 Architecture를 머리 속에 그려보면 아래와 같다.

OAuth Authorization Architecture

간단히 그려보면 아래와 같은 일반 구조와 같다.

JWT를 만드는 역할을 Service에서 할 일은 아니지만, Authentication Service에서 만들어준 JWT 값을 가져오고, 올바른지 확인하는 역할은 Client Service의 역할이다. 헤더에서 이걸 가져오고 Public Key를 관리해서 이걸 검증하는 동작은 비슷한 패턴이다. 각 서비스에서 이를 구현하는 건 반복적인 코딩이 될 수 밖에 없다. 그래서 이걸 Spring-Security에서 좀 더 쉽게 할 수 있도록 지원한다.

 

Validating with Spring Security

자, 그래서 인증이 올바른지 체크하는 Spring Security 모듈을 처리해보자.

Maven setting

pom.xml

 

Configuration in Action

Configuration coding in the spring-boot project
이렇게 설정이 준비되면, spring 코딩을 마무리할 시점인갑다.

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable()
                .authorizeRequests().antMatchers(whitelistedUriPatterns()).permitAll().and()
                .authorizeRequests().anyRequest().authenticated().and()
                .oauth2ResourceServer().jwt();
    }

    private String[] whitelistedUriPatterns() {
        return new String[] {
                "/health",
                "/swagger*/**", "/webjars*/**", "/v2/api-docs"
        };
    }
}

이렇게 처리하면 된다.

주목할 점은 특정 URI들의 경우에는 Load balancing과 내부 테스트등을 위해서는 별도의 인증을 타지 않도록 설정해야 한다는 점이다. 여기에서는 /health endpoint가 대표적이다. 만약 이걸 예외처리하지 않으면 L7 Switch의 경우, 설정된 endpoint가 비정상(401을 반환할 것이기 때문에)이라고 판단해서 Target group에서 이를 빼버리기 때문이다. 예제에서는 Swagger 관련 설정도 예외 처리를 했다.

application.yml

Configuration과 관련된 코딩을 반영했다면, 내부적으로 JWT Token의 Validation을 위한 Public Key endpoint를 추가로 잡아준다. spring.security.oauth2.resourceserver.jwt.issuer-url 속성에 Public Key가 존재하는 URL을 설정해주면 된다.

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://auth.games.com/public

 

물론 이렇게 하지 않고, 별도로 따로 Filter를 하나 만들어서 손수 체크할 수도 있다.

Authorization JWT handling

이렇게 설정을 하고 나면, JWT 객체안에 있는 특정 필드 혹은 객체 정보를 @AuthenticationPricipal annotation을 활용해 끄집어낼 수 있다. JWT내의 특징 필드를 끄집어낼때는 해당 field의 이름을 지정해서 추출한다. 아래 예제에서 subject라는 필드는 에 포함된 sub 필드를 의미한다.

@PostMapping("/access/product/{productId}")
public SecurityAction verifyClientExecution(@AuthenticationPrincipal(expression = "subject") String id,
                                            @PathVariable("product") String product,
                                            @RequestBody SecurityInfo info) {
...
}

만약 JWT 전체 값을 참조하고 싶은 경우에는 아래와 같이 Jwt 클래스(org.springframework.security.oauth2.jwt.Jwt)를 파라미터로 지정해서 사용 가능하다.

 

Appendix

  •  – MVC 모델에서 Argument Resolver 및 특정 타입으로의 타입/모델 변경에 대한 샘플을 제공한다.

 

]]> 782
블로그가 털렸네 /index.php/2018/03/08/poor-security-stolen-database/ Thu, 08 Mar 2018 09:22:16 +0000 /?p=529

Continue reading ‘블로그가 털렸네’ »]]> 회사에서 개발을 하면서 항상 고려할 최우선 순위 가운데 하나가 보안이다. 특히나 서비스내에서 제공되는 정보 가운데 개인과 관련된 민감한 정보가 있다면 보안은 최우선 순위 고려 사항이다. 회사에서 개발을 할 때는 이걸 항상 가장 먼저 생각하는데, 최근에 정말 어이없는 일을 겪었다.

뭐냐하면… 짜잔~

그렇다. 이 홈페이지가 털렸다. 친절하신 해커분께서 WordPress DB로 사용하는 MySQL 서버에 접속하셔서, 데이터를 몽땅 다운로드 받으신 다음에 모든 테이블을 싹~ 정리해주셨다. 그리고 친절하게 READ.me 라는 테이블에 비트코인을 보내주면 데이터를 보내주겠다는 친절한 멘트를 남겨주셨네?

처음에는 이상하게 홈페이지 방문자 카운트가 나오지 않았다. 단순 플로그인 문제로 생각했다. 몇 달동안 플러그인 버전업이 됐다는 메시지를 봐왔으니까. 홈페이지의 방문자 수를 보는게 일상의 소소한 재미였는데, 몇 일이 지나도 숫자가 그대로니 고쳐야겠다는 생각이 들었다. 그런데 이게 왠걸? 업데이트 하라는 플러그인들을 죄다 업데이트했는데도 여전히 카운트가 나오질 않네? 뭐지???

Frontend 단에서 JS 문제가 있는가 싶어서 크롬 개발자 모드로 들어가서, Refresh를 했다. 음! DATABASE CONNECTION ERROR 라는 아주 불친절한 문구가 떡 하니 나온다. 그 사이에 암호도 변경한게 없고, AWS에 돈도 따박따박 내고 있었는데 이게 뭐지? 내가 실수로 Security Group 설정을 변경했나? 장비에 들어가서 telnet 으로 접속되는지 확인해봤지만 정상이다.

$ telnet db.rds.domain.name 3306

데이터베이스 서버로 TCP 접속은 되는데, 데이터베이스 오류면 암호가 안먹는거라는 것 같은데? Mysql Workbench로 데이터베이스에 접속해봤더니 덜렁 READ.me 라는 테이블만 하나 덜렁있다 순간 내가 RDS Instance의 Security Group의 설정을 0.0.0.0/0 으로 설정했던 기억이 머리를 커다란 망치로 때린다. 왜 EC2 Instance는 Security Group을 나름 생각한다고 잡아두고, RDS는 이렇게 바보처럼 해놨을까?

망!!

네이버 퇴사하면서 그 이후에 작성한 글들은 죄다 여기에서 작성해왔는데 다 날라간건가? 글의 절대적인 가치는 보잘것 없겠지만 그래도 하나 쓰더라도 나름 신경을 썼던 글들이었다. 무엇보다도 지금까지 라이엇에서 한 작업들 가운데 까먹지 말자라는 차원에서 기록해둔 것들이었는데. 이대로 날려버린건가?

좌절모드에서 헤매고 있었는데, AWS Console에서 RDS 설정을 보니 Snapshot 백업을 설정해 둔게 기억났다. 부랴부랴 들어가서 확인해보니 최근 7일내의 데이터가 일 단위로 백업되고 있었다. 언제 해킹을 당했는지 WordPress 사이트에서 일단위 Visiting count 이력을 보니 아직 하루쯤 여유 시간이 남았네!


기쁘다 구주오셨다!

서둘러 Snapshot을 가지고 RDS Instance를 생성시켰다. 일단 생성시킨 RDS 접속해서 테이블의 상태를 확인해보니 모든 테이블과 데이터들이 온전히 살아있다. 다행히 최근에 이런 저런 일들이 있어서 글을 거의 안쓰고 있었는데 빠진 데이터없이 온전히 글들이 살아있다. 휴~ 일단은 다행이다.

  • 먼저 Security group의 설정부터 잡는다. EC2 Instance에서만 접근 가능하도록 했다. 물론 Workbench와 같은 쿼리 도구들을 바로 접속할 수 없긴 하지만 필요한 경우에는 AWS Console에서 필요할 때마다 Security Group의 설정을 변경하는게 아주 많이 안전하다.
  • 다음으로 정말 단순했던 데이터베이스 접근 암호를 수정한다. 기억력의 한계를 절감하는 나이라 그런지 사이트마다 암호를 달리 설정하는데 한계가 있다. 5 ~ 6 가지 정도의 암호를 몇가지 룰을 가지고 변형해서 사용해왔다. 털린 암호는 2개의 영문 단어를 조합한 형태였는데 Dictionary를 가지고 대입 공격을 하면 쉽게 뚫릴 수 있는 구조라는 생각이 들었다. 안타깝게도 MySQL에서는 암호에 특수 문자를 사용할 수 없다. 그럼에도 암호에 대한 복잡도를 높일 필요성이 있고, 3개의 단어와 숫자들을 조합해서 암호를 변경했다.
  • 가장 중요한 포인트인 것 같은데 암호를 변경할 날짜를 캘린더에 적어뒀다. 암호는 자주 바꿔주는게 최선인 것 같다. 바꾼 암호를 까먹지 않는다는 전제하에.

이렇게 설정을 마치고, WordPress의 연결 정보를 업데이트했다. 그리고 이렇게 글을 쓴다.

이번 일을 겪으면서 돌이켜 생각이 든다. 과연 나는 제대로 된 보안 정책을 가지고 개발을 하고 있는가? 귀찮기 때문에 혹은 설마라는 생각 때문에 어느 포인트에서 보안과 관련된 허점을 만들어두지는 않았을까? 이번 일을 겪으면서 보니 모를 일이라는 생각이 불현듯 스친다. 신규로 개발한 서비스들에 대해서는 모두 보안 리뷰를 받았다. 전문가 팀이 리뷰를 해준 사항이기 때문에 일정 수준은 안전하다라는 생각이 들기도 하지만 이 생각 자체도 만약의 사고에 스스로를 위한 면피가 아닐까 싶다.

개발할 때 자만하지 말고 개발하자. 그리고 잘하자. 쉬운길이 아니라 제대로 된 길로 가자.

– 끝 –

]]> 529