오픈소스 CATS로 간단히 퍼즈(Fuzz) 테스트

퍼징이란 퍼즈(Fuzz) 또는 퍼징(Fuzzing)이란 블랙박스 테스트의 한 방법으로 정상/비정상 데이터를 자동으로 생성하고 함수나 API에 전달한다. 어떻게 보면 암호 해독(crypto anaylysis) 과정과 비슷하게 보이지만 퍼징은 프로토콜이나 입력 데이터 타입에 의존적이다. 그래서 실패한 테스트 결과들은 암호 해독과는 다르게 실제로 유의미하다. 여담으로 몇년전 대기업에서는 유사한 테스트를 수행하기 위해서 전문 인력까지 두었었다. 그때도 도구를 사용하긴 했지만 테스트 설정부터 수행 후 보고서 … Read more

에드워드 스노든 그리고 샌드웜

연말에 시간 가는줄 모르고 읽었던 보안관련을 소개한다. 이 두책은 서로 얽혀 있는 부분이 있지만 스노든의 책은 정부의 민간인 감청에 대해서, 샌드웜은 정보기관 사이의 사이버전쟁에 대해서 다룬다. 결론은 IT분야 종사자라면 반드시 한번 읽어 보기를 추천한다. 안타까운건 두책 다 원서인데 스노든의 책은 아마도 번역이 될 것 같지만 샌드웜은 저자가 2014년에 출간한 책도 한국에 없는 걸 보면 번역 될 것 같지 않다. … Read more

더 라스트 오브 어스 파트2 리뷰

이 리뷰는 강력한 스포일러를 담고 있습니다. “영화 같은 게임”으로 유명한 더 라스트 오브 어스 파트2(이하 라오어2)가 올해 6월에 출시되었다.  나중에 플레이하려고 맘먹었기 때문에 출시 직후 최대한 리뷰는 읽지 않으려고 했지만 너무나 실망이라는 소식과 게임 관련 웹진의 후한 리뷰에 속았다는  사람들의 반응만 전해 들었다.  미루다가 아마존에서 29달러 세일을 하기에 빠르게 구매해서 플레이해 보았는데, 웬걸. 개인적으로는 정말 집중해서 … Read more

클린 (리액티브) 코드

RxJS merge operator

클린코드(Clean Code)는 소프트웨어 엔지니어링 분야에서 굉장히 의미가 깊은 책이다. 클린코드로 많은 개발자들이 자기가 작성한 코드의 정량적인(Quantitative) 부분 뿐만 아니라 정성적인(Qualitative) 부분도 신경쓰게 되었다. 책을 관통하는 주제는 일관되다. 사람이 읽기 쉬운 코드가 유지보수가 더 쉽기 때문에 높은 품질을 가진다는 것이다. 나는 3~4년에 걸쳐 스프링 WebFlux를 사용해 증권앱의 서버 그리고 GraphQL의 게이트웨이 프로젝트를 진행했다. WebFlux를 사용한 결과에 충분히 만족했고 Graphql … Read more

채식을 시작한 이유

2020년 초부터 식단을 채식으로 바꿨는데 다른 사람들에게 이제 채식해 라고 말했을 때 반드시 갑자기?” 라는 질문을 여러번 받았기에 이후에 조금 더 설득력있게 대답하기 위해 글로 남겨본다.

계기는 비욘드미트

비욘드미트는 2009년에 Ethan Brown이 창업한 대체육 회사이다. 상대적으로 흉내내기 쉬운 햄버거 패티, 소시지, 연육등을 대형마트와 자사 온라인 쇼핑몰을 통해 판매하고 있다. 물론 현재는 실제 고기보다 가격도 비싸며 맛도 떨어진다. 그럼에도 불구하고 2020년 초에 처음으로 맛본 비욘드 미트는 잊고있었던 채식에 대한 의지를 다시한번 일깨워줬다. 호주의 유명 햄버거 체인에서 판매하는 비욘드 미트를 사용한 햄버거는 실제 햄버거와 80% 정도 유사하고 가격도 일반 햄버거와 동일했다. 그리고 집에서 비욘드 미트를 조리했을 때 사료(?) 비슷한 향기가 나서 조금 맘에 걸렸지만 일반 식당에서 이렇게 대체육 옵션을 제공한다면 주저없이 선택할 수 있을 것 같았다. 바로 그때 이번에는 정말 채식이 가능하겠다 라는 느낌이 왔다.

갑자기 왜 채식을?

무엇을 먹고 살 것인가 라는 의문은 존재론적인 고민들과 굉장히 유사하다. 많은 사람들이 크게 괘념치 않고 살아가지만 한번 의문을 품으면 그야말로 스스로 질문과 답을 반복하게 상태에 빠지고만다. 처음 채식을 해야겠다고 결정했을 때 여러가지 이성적이고 감성적인 요인들이 포함되어 있었다. 그중에서도 시작은 특히 스스로 동물을 좋아한다고 말하면서 삼시세끼 소,돼지,닭을 먹는것에 대한 자기배반적인 감정이었다.

영양 넘치는 채식

시작은 그러했지만 나도 처음에는 채식으로 성인 남성에게 적절한 영양소를 공급할 수 있을까? 하는 의문을 가졌다. 웹으로 여러가지 조사를 진행하고 몇달간 해본결과 그런 고민은 기우였음이 밝혀졌다. 이상한 출처를 알 수 없는 자료들 보다 영국의 국민건강보험에 해당하는 NHS에서 내놓은 자료들이 훨씬 신뢰가 갔다. 물론 영국과 한국간의 인종적인 차이는 있겠지만 한국에서 이뤄지는 다양한 연 채식을 건강식으로 받아들이기 시작한 것 같다. 기사 “금연·예방접종·채식·운동하면 암 70% 예방”

고기를 먹었을 때보다 섭취하는 식품의 영양에 신경 쓰게 된 점도 아주 긍정적인 변화이다. 그러면서 한국의 식습관이 채식에 유리한점도 발견할 수 있었다. 그 예로 비타민 B12는 채식을 하게 되면 결핍을 주의해야하는 영양소로 자주 언급되는데 한국인들이 자주먹는 김에 바로 그 B12가 풍부하게 포함되어 있다는 것이다.

아직 1년이 되지 않았고 건강검진을 받지 않은 상태라 채식이 어떻게 내 몸에 영향을 끼쳤는지 수치화 할 수는 없지만 느끼기엔 아주 건강해진 것 같다. 채식 이전에도 특별히 어디가 아프거나 한것은 아니었지만 눈에 띄는 변화중 하나는 채식후에 배가 아팠던 적이 한번도 없었다는 것이다. 예전에 고기를 먹고 특히 밤에 소화가 잘 안되거나 아랫배가 아파서 밤에 자다가 깨는 경우도 있었는데 정말 화장실에서 기절 직전까지 간 경우도 있다. 채식을 하고는 몸안이 굉장히 깨끗해진 느낌인데 몸 뿐만 아니라 냉장고와 쓰레기통까지 깔끔해진 것은 덤이다.

기후변화 대응

개인적인 범위의 변화뿐만 아니라 지구를 위해서도 채식은 권장할 만하다. 내가 살아오면서 느낀 환경운동이 가지는 문제점중 하나는 개인이 무엇을 해야할지 뚜렷하지 않은 경우가 많고 정책의 변화나 의식의 전환만을 요구하는 것에서 그친다는 것이다.

반면 채식은 바로 실천 가능하다. 전세계 인구가 기후변화의 주체임을 깨달을 수 있다는 것이다. 채식으로 전환하거나 섭취하는 육류를 조금이라도 줄이는 것만으로도 기후변화를 막는데 힘을 보탤 수 있다. 가끔 축산이 온실효과에 끼치는 영향이 확실히 않다고 채식의 기후변화 대응효과를 축소하려는 사람들을 자주 보았다. 농축산이 온실효과에 끼치는 영향은 15%에서 50% 까지 다양한 연구 결과가 보고 되고 있는데 이산화탄소, 메탄등 각종 요인들이 서로 영향을 끼치고 있기 때문에 앞으로도 정확한 숫자를 알아 내는 것은 힘들 것이다. 설령 농축산이 온실효과에 10%만 기여 한다고 해서 당장 전기차를 구매하는 것이 채식보다 더 기후변화에 앞장서는 대응일 수는 없다. 모든 부분에서 걸쳐서 노력을 해야하지만 화석연료 사용을 줄이는 부분은 각 나라의 정책 방향에 크게 의존하는 것이 사실이다. 예를들어 전기차가 사용하는 전기가 석탄으로 생성된 것이라면 결국 제자리이다. 결국 채식은 개인이 오늘당장 실천할 수 있는 가장 손쉬운 기부변화 대응 행동이다. 기사 – 기후위기 시대, 채식이 지구를 살린다

채식, 쉽지는 않다

나는 동물들을 사랑하지만 다른 사람들도 다 그런것은 아니며 꼬리에 꼬리를 무는 질문을 해서 그럼 식물들은 먹어도 괜찮은 것인가? 라고 듣게 될 수 있다. 결국 뭔가를 먹는다는 행위가 유기물을 분해해서 자신의 영양소로 만드는 과정이 아니던가. 채식주의자들은 필요한 영양소들은 이미 식물에 인간이 섭취할 수 있는 상태로 존재하기 때문에 그것을 섭취한 동물들을 다시 도축해 먹는 과정은 불필요하다고 이야기 하지만 솔직히 나를 포함한 많은 채식주의자들의 채식을 결심하게 된 계기는 동물에 대한 애착에서 시작된 경우가 많다. 앞서 언급했듯이 채식을 시작한 요인은 동물에 대한 애착, 감성적인 요인이다. 반대로 육식을 끊을 수 없었던 만드는 감성적이고 문화적 요인도 존재한다.

회식은 삼겹살아니면 치킨..쩝쩝

한국회사에서 일 마치고 먹었던 삽겹살과 치킨. 과연 다음에 똑같은 기회가 생겨도 저항할 수 있을까?

일단 나는 현재 채식을 하기 굉장히 좋은 조건임에는 분명하다. 우리 회사는 COVID와 상관없이 앞으로도 재택근무가 기본이 될것임을 발표했다. 그외에 나라별 차이는 장볼때나 식당에서 많이 드러난다. 호주는 한국보다 채식인구의 비중이 높으며 개인의 식단에 크게 신경쓰지 않는 문화다. 그리고 재배 가능한 땅이 넓다 보니 마트에 지역산 채소나 과일들이 많으며 (물론 고기도 많다) 아직은 시범적이긴 하지만 다양한 대체육들도 눈에 띄인다. 지금은 운영하지 않는 회사 카페테리아도 샐러드바 형태여서 개인의 식단에 맞춰 점심식사가 가능했다.

반면 한국에서는 학교, 회사, 군대등 다양한 장소에서 급식을 하는데 채식을 유지하는 것은 개인에게 큰 시련이 될 수도 있다. 다행히도 한국도 집단 급식을 하는 학교나 군대에서 채식을 선택할 수 있도록 바뀌고 있는데 이는 점점 한국도 개인의 개성과 다양성을 존중하는 방향으로 옮겨가고 있다는 증거로 보여진다.

서울 학교급식에 ‘채식선택권’ 도입…”초중고 점차 확대”

[단독]군대서도 비건 급식 먹는다… 채식주의자, 짬밥을 바꾸다

그럼에도 한국 직장인들의 생활패턴을 생각해보면 한국에서 채식을 유지하는 것은 굉장히 힘들다. 점심이나 회힉등 다 같이 식사하는 자리가 많은데 그런 식당에는 채식메뉴가 없을 확률이 굉장히 높다. 그리고 아직 한국의 외식비는 직접 조리하는 것에 비해 많이 비싸지 않다. 집에서 스스로 요리를 할 수 있다면 채식식단을 꾸리기 더 쉬울테지만 만원으로 고기를 먹을 수 있는데 힘들게 직접 재료를 사서 조리하기엔 동인이 부족하다.

채식을 유지하기 위해선

그래서 초기에는 좀 잘알려진 채식관련 식당들을 다니면서 다양한 채식 음식을 먹어보는게 중요할 듯 하다. 나도 채식을 시작하고 가장 힘들었던게 메뉴구성이었다. 경험해본 메뉴가 적으니 매일 같은 메뉴가 반복되는 것인데 여러나라의 다양한 음식들을 접해서 스스로 만들 수 있는 채식의 구성을 최대한 늘려놔야 한다. 그래도 역시 고기가 없이는 만들기 힘든 음식들이 생각날때 채식의 풍미를 더해줄 수 있는 대체육이 정말 가뭄에 단비같은 존재이다. 왜 채식주의자라고 스팸, 소시지, 베이컨 등을 맛있다고 느끼지 못하겠는가. 대다수의 사람들에게 고기를 먹지말라고 한다면 그사람이 가진 얼마 되지 않는 기쁨을 빼앗는 것일수도 있다. 사람은 항상 건강한 방식만을 찾아서 생활하는 것은 아니기 때문에 대체육은 채식에서 대항해시대의 향신료와 같은 역할을 할 수 있을 것이라고 믿는다.

한국도 남의일이 아니다

채식에 대한 관심이 높은 미국이나 호주는 식량 자급률이 굉장히 높은 편인데 한국은 자급률이 50%가 되지 않는다. 참고 – [데스크의눈] 코로나發 식량 위기론 다른말로 한국은 세계 식량 사정에 따라 비자발 적으로 식습관을 급격하게 바꿔야 할수도 있음을 의미한다. 그게 채식이 될지는 아무도 모르지만 적어도 육식보다는 자급률을 높일 수 있는 채식 바람에 대비해놓아야 하지 않을까. 한국에서도 채식하는 인구가 많이 늘어사서 다양한 음식들이 새로 개발 되고 식당에서도 자연스럽게 채식 옵션이 제공될 수 있기를 기대해본다.

아치유닛(ArchUnit) 테스트

아치유닛(ArchUnit)을 사용하는 이유

“두 패키지 사이에 순환 참조(Circular Dependency)가 존재합니다. 변경이 필요해요.”

“@SpringBootTest 어노테이션을 사용하는 통합 테스트 코드는 test 폴더가 아니라 integration-test 폴더에 위치해야합니다.”

“Service 레이어는 Controller와 Model 패키지 에서만 접근해야 합니다”

팀내 경력이 오래된 시니어 개발자는 위와 같은 커멘트를 작성할 때가 많다. 그러면서 왜 그래야 하는지 코드나 패키지 단위로 다이어그램을 그려서 어떻게 컴포넌트들이 서로 작동을 해야하는지 설명한 경험이 있지 않은가?

새롭게 팀에 들어왔거나 해당 지식을 가지지 않은 사람들은 제일 경험이 많은 사람이 친절하게 알려주는 것이 팀내 지식 공유 차원에서는 제일 바람직할 것이다. 하지만 확장 가능하지 않고 팀내 고급인력의 끊임 없는 관심을 요구한다. 갑자기 사람이 늘어나거나 담당자가 휴가를 가버리거나 퇴사하면 잘 작동하지 않는 모델인 것이다.

우리는 이미 유사한 상황들에 많이 대처해봤다. 아치유닛을 사용하면 아키텍처상의 결정사항이나 결함들도 통합 빌드의 한 부분으로 테스트를 작성하고 자동화 하는 것이 가능하다.

홈페이지에서도 언급하고 있듯이 아치유닛이 아니어도 AspectJ나 CheckStyle, Findbugs를 사용해서 유사한 테스트를 수행할 수 있다. 하지만 해당 도구들은 조금 더 범용적인 성격을 가지고 있기 때문에 코드의 구조를 분석해서 읽기 쉬운 테스트를 작성하기 위해서는 아치유닛을 사용하는 것을 것을 추천한다.

아치유닛 구성

아치유닛은 Core 레이어, Lang 레이어, Library 레이어가 존재한다. Core API를 통해 대상을 특정하고 Lang API를 사용해 규칙을 정의한다. Library 레이어는 미리 정의된 규칙들, 예를들어 3 Tier 아키텍처, 6각형 아키텍처(Hexagonal Architecture) 등을 위한 규칙들을 제공한다. 아직은 실험적인 상태로 보이며 추후 확장의 여지가 있는 부분이다.

Core 레이어의 ClassImporter

리플렉션과 유사한 기능을 제공하는 Core레이어에서는 ClassImporter가 가장 중요한 API들을 제공한다. ClassImporters는 컴파일된 클래스들을 불러오기 위해 사용한다. 다음 코드는 com.book 패키지내의 클래스중에 Jar나 테스트 폴더등을 빼놓고프로덕션 코드만 대상으로 지정하는 설정이다.

ClassFileImporter()
        .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_ARCHIVES)
      .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_JARS)
        .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)
        .importPackages("com.book")

Lang 레이어는 아키텍처 규칙을 정의하는 API들을 제공한다. 규칙을 정하고 나면 다음과 같이 실제 검증을 수행한다.

JavaClasses importedClasses = new ClassFileImporter().importPackage("com.myapp");
ArchRule rule = // 아래 예와 같이 다양한 룰 생성
rule.check(importedClasses);

패키지 의존성 확인

service 패키지는 controller와 resource에서만 접근 가능하다.

classes()
   .that()
   .resideInAPackage("..service..")
   .should()
   .onlyHaveDependentClassesThat()
   .resideInAnyPackage("..controller..", "..resource..")

클래스 의존성 확인

*Service 클래스는 Controller 클래스 에서만 접근 가능하다.

classes()
  .that()
  .haveNameMatching(".*Service")
  .should()
  .onlyBeAccessed()
  .byClassesThat()
  .haveSimpleName("Controller")

클래스와 패키지 관계 확인

Book으로 시작하는 클래스는 com.book 패키지에 위치해야 한다

classes()
  .that()
  .haveSimpleNameStartingWith("Book")
  .should()
  .resideInAPackage("com.book")

상속 관계 확인

Connection 인터페이스를 구현하는 클래스는 이름이 Connectiond으로 끝나야한다.

classes()
  .that()
  .implement(Connection.class)
  .should()
  .haveSimpleNameEndingWith("Connection")

EntityManger클래스로 할당 가능한 클래스들은 persistence 패키지에 위치해야 한다.

classes()
  .that()
  .areAssignableTo(EntityManager.class)   
  .should()
  .onlyBeAccessed()
  .byAnyPackage("..persistence..")

주석 테스트

com.book 패키지 중에서도 “build/classes/kotlin/test” 폴더에 위치한 테스트들은 SpringBootTest를 사용해서는 안된다.

 classes()
    .that()
    .resideInAPackage("com.book")
    .should()
    .notBeAnnotatedWith(SpringBootTest::class.java)
    .check(ClassFileImporter()
         .importPath("build/classes/kotlin/test"))

레이어 테스트

논리적인 레이어를 구성해서 그 관계를 검증한다. 패키지로 구분된 controller, service, persistence 레이어를 각각 정의하고 각 레이어 별로 접근가능한 레이어들을 정의한다.

layeredArchitecture()
    .layer("Controller").definedBy("..controller..")
    .layer("Service").definedBy("..service..")
    .layer("Persistence").definedBy("..persistence..")
    .whereLayer("Controller")
    .mayNotBeAccessedByAnyLayer()
    .whereLayer("Service")
    .mayOnlyBeAccessedByLayers("Controller")
    .whereLayer("Persistence")
    .mayOnlyBeAccessedByLayers("Service")

순환 참조 테스트

패키지 com.book 의 하위 패키지들을 slice로 구성해서 각 slice들이 순환 참조 하지 않는 지 검사한다.

slices()
 .matching("com.book.(**)")
 .should().beFreeOfCycles()
 .check(javaClasses)

유용한 애자일 의식들 (Agile Rituals)

이전 아틀리시안 취업 후기에서 짧게 언급했지만 아틀리시안에서는 애자일 관련된 미팅을 할 때 사용하는 여러가지 템플릿들이 존재한다. 보통 애자일 의식(ritual, ceremony)이라고 하면 스프린트 플래닝, 데일리 스크럼, 회고 등을 떠올리는데 그 외에도 팀이 업무를 하면서 마주치는 여러가지 상황에 맞게 활용할 수 있는 다양한 의식들이 존재한다.

애자일 의식으로 얻는 것

정확한 의사전달

이렇게 미리 정의된 도구들을 사용할 때 얻는 가장 큰 장점은 구성원간 의미전달이 아주 명확해 진다는 점이다. 예를 들어 “이번주 금요일에 회의 있어요”. “오늘은 일하는 날이에요” 라고 말하는 것 보다 “이번주 금요일에 스파링 있습니다”, “오늘은 GSD날이에요”. 라고 말하는게 세세한 차이까지 전부 전달할 수 있다.

생산성 증가

선진국을 중심으로 근로자의 월 평균 근무시간은 계속 줄어들고 있다. 어떻게 적게 일하면서 더 높은 생산성을 유지할 수 있을까? 회사 전체의 생산성을 위해서는 위해선 여러 사람이 모이는 회의나 의사결정의 생산성이 무엇보다 중요하다. 팀원이 5명인 경우 1시간 걸릴 의사 결정을 3시간 걸려서 끝낼 경우 10시간의 추가 근무가 필요하다.

결과물 명확화

각 의식 별로 단계별 결과물이 확실히 정해져 있기 때문에 회의가 중간에 다른 길로 샐 우려가 적다. 각 참가자들은 회의에 앞서 어떤 내용을 준비해야 하는지 진행되면서 어떤 행동을 해야하는지 미리 알 수 있게 된다. 이는 여러 사람이 모였을 때 쓸데 없는 시간 낭비를 줄일 수 있다.

10년전에 한국 회사에 근무할 때 목적을 알 수 없는 미팅이 참 많았다. 2-3시간을 내리 미팅을 하지만 회의록만 늘어날 뿐 결정된것은 하나 없고 미팅이 끝나도 구성원들이 무엇을 해야할지 감을 잡을 수 없는 그런 상황, 참 많이 겪어봤다. 그런상황에서는 단순히 지칭하는 용어를 미팅,회의에서 회고, 데일리 스크럼 바꾸는 것 자체가 큰 효과를 가진다. 기술회사에서 근무하는 대부분의 사람들은 해당 용어를 접했을 때 회의의 목적이 무엇인지, 참가자로서 어떻게 행동해야 하는지 대부분 바로 이해할 수 있다. 데일리 스크럼이나 회고 같이 업계 수준에서 사용하는 의식들은 훨씬 도입하기 쉽다.

아틀라시안의 팀플레이 북은 아틀라시안 내부에서 자주 사용되는 여러 의식들을 플레이(Play) 형태로 제공한다. 여기서는 플레이 북의 의식들을 위주로 어떤 상황에서 활용할 수 있는지 알아 본다. (위키삽입)

애자일 리츄얼

DACI

컨플루언스 DACI 템플릿

의사 결정을 위한 애자일 의식은 다씨(DACI)라고 부르는데 팀단위나 그 상위 조직의 의사결정을 효율적이고 효과적으로 할 수 있게 도와준다. 아틀라시안에서는 보통 개발자가 아키텍처의 변경이나 여러 팀에게 영향을 미치는 의사 결정을 할때 사용한다. 다씨는 Driver(드라이버), Approver(승인자, 주로 엔지니어링 매니져 또는 아키텍트 ), Contributors(기여자, 동료 개발자들), Informed(결정 사항을 알아두면 좋은 사람들)로 구성 된다.

1단계는 의사 결정을 추적하기 위한 컨플루언스 페이지를 만든다.

2단계에서는 10분 정도 시간을 할애해 역할을 할당한다. 보통 다씨를 여러번 수행 했고 역할이 대략적으로 이미 정해져있다면 이 단계는 생략 한다.

3단계에서는 드라이버가 각 항목 들을 입력 한다. 모든 항목 들을 반드시 채워넣어야 하는 것은 아니다. 템플릿은 템플릿일뿐 상황에 맞게 응용한다.

  • 의사 결정 데드라인
  • 결정이 필요한 배후사정, 컨텍스트.
  • 현재 상태, 결정중, 결정됨, 연기 등
  • 의사 결정을 위해 실행한 관련 조사
  • 선택 사항들. 각 항목들의 장단점, 예상 비용 또는 필요한 노력등을 표시한 테이블
  • 추천, 기여자들의 추천 사항
  • FAQ

이렇게 작성한 페이지를 팀원들에게 공유하고 커멘트를 통해 의견을 받는다. 보통 이와 같은 방식으로 특별한 회의 없이 컨플루언스 페이지 만으로 여러 다양한 의사 결정이 가능하다. 다시는 의사 결정을 효율적이고 신속하게 만들어 줄 뿐만 아니라 기록을 확실히 남김 수 있는 장점도 존재한다. 이는 Github에서 AWR을 작성하는 이유와 비슷하다.

참조

의사 결정 템플릿

ADR(Architecture Decision Records)을 써야하는 이유

프로젝트 포스터(Project Poster)

프로젝트 포스터 템플릿
컨플루언스 프로젝트 포스터 템플릿

서비스를 개발하면서 여러가지 새로운 프로젝트들을 진행하게 되는데 그때 관련 정보의 시작점이 되는 의식이 프로젝트 포스터이다. PO(Project Owner)와 구성원들을 나열한뒤 프로젝트가 풀고자 하는 문제 영역에 대해서 서술한다. 검증(Validation) 영역에서는 현재 알고 있는 것과 모르는 것을 구분한다.

이 문서는 주로 PM이나 PO들이 주로 작성을하는 문서다. 개발자로서 프로젝트 포스터는 사내에 진행되는 프로젝트 들의 개요를 파악하는데 아주 유용한 문서가 된다.

참조

프로젝트 포스터 템플릿

스파링

애자일 스파링은 동료들에게 피드백을 구할 때 사용하는 의식이다. 스파링의 주제는 여러가지가 될 수 있다. 개발시 진행 방법에 대해서도 스파링을 할 수 있지만 주로 스파링의 대상이 되는건 제품이나 디자인 관련된 주제들이 많다. 스파링에 앞서서 참가자들에게 관련 자료 링크를 첨부한다. Garbage In, Garbage out , 입력이 좋아야 출력이 유의미하다. 회의 시간은 30분으로서 다음과 같은 절차를 거친다

1단계는 참가자들에게 원하는 피드백의 수준과 비평받길 원하는 영역에 대해서 언급한다. 예를 들어 너무 세세한 디테일에 신경쓰지 않아도 된다는 언급을 해주면 참석자들의 시간을 아낄 수 있을 것이다. (5분)

2단계는 다루는 주제에 대해 설명한다. 시간이 없으므로 내용은 모두 간략하게. 큰 방향을 잡기 위한 정보만 제공하고 주장을 정당화 하기 위해서 사용하지 않는다. (5분)

3단계는 참가자 들에게 피드백을 받는 시간이다. 참가자들은 직접적인 피드백이나 질문을 포스트잇을 통해 제출한다. 진행은 조용하게 필요한 질문만 한다. 그렇지 않으면 또 시간이 길어진다. (10분)

4단계는 3단계에서 나온 내용들을 가지고 토론하는 시간이다.

5단계는 유의미한 질문이나 피드백을 정리하고 필요한 경우 다음 스파링 세션을 정한다.

스파링에서 다루기 적합한 주제로는 성공에 대한 정의, 문제 영역에 대한 정의, 우선순위등이 될 수 있다. 문제는 발견했지만 어떻게 성공을 정의할 것인가. 수치는 있지만 목표를 어떻게 정해야 할지 모를 때. 스파링을 통해 자신이 보지 못한 답을 얻을수 있을 것이다. 컴포트 존에서 나올 수 있도록 도와주는 것이 애자일 스파링이다.

참조

https://dzone.com/articles/sparring-how-to-get-peer-feedback-you-can-actually

https://www.atlassian.com/team-playbook/plays/sparring

GSD(Get Shit Done)

GSD는 실제 문제 해결에 집중하는 의식이다. Get Stuff Done, Get Things Done등과 같이도 부른다. 내 경험상으로는 GSD를 위해 별도의 문서작성이 필요 하진 않지만 다른 팀원 들을 위해서 GSD가 필요한 이유와 원하는 커뮤니케이션 방법등을 따로 남겨두면 더 좋을 것이다. 현재 팀에서는 주로 월요일을 GSD 하는 날로 정하고 있다. 주말에서 돌아오자마자 플래닝 미팅을 한시간 하게 되면 팀원 들은 보통 괴로워 한다. 그래서 월요일을 GSD로 정해 스프린트를 마무리하고 보통 화요일날 플래닝 미팅과 회고를 진행한다.

참조

https://www.atlassian.com/team-playbook/plays/gsd-day

팀 상태 모니터

팀 상태 모니터 템플릿

프로젝트 팀의 전반적인 상태를 알아보기 위해 사용하는 의식이다. 팀 멤버들은 각자 주어진 팀 상태 평가에 대해 답을하게 되고 개선이 필요한 영역을 도출하게 된다. 미진한 부분을 개선 시키는 것이 목적이며 해당 영역에서 실행 가능한 방법이 무엇인지 도출할 수 있게 된다.

컨플루언스 상태 모니터 템플릿

https://www.atlassian.com/team-playbook/health-monitor

엘리베이터 피치

진행하는 프로젝트의 일관되고 간단히 설명하기 위해 진행하는 의식. 각 팀원들이 포맷에 맞춰서 입에 달라 붙는 문구를 나열한다. 마지막에 투표를 통해 하나의 통일된 표어를 결정한다.

참고

https://www.atlassian.com/ko/team-playbook/plays/elevator-pitch

https://ko.wikipedia.org/wiki/엘리베이터_피치

5 Why 분석

5 Why 분석은 장애 원인 분석이나 회고(Postmortem)시에 많이 사용하는 의식이다. 이 의식은 실제 문제가 무엇인지 명확히 드러나 있지 않았을 때 5 Why 방법을 사용해 문제 도출을 위해 사용한다.

참고

https://www.atlassian.com/team-playbook/plays/5-whys

업무 능력 계획(Capacity Planning)

업무 능력 계획 템플릿
컨플루언스 업무 능력 계획 템플릿

팀의 처리 능력을 확인하기 위해서 수행하는 의식. 첫번째로 팀원 들에게 각자 1주일 동안 수행하는 업무를 나열하게 한다. 해당 작업에 얼마 만큼의 시간을 소모하는지 예상치를 적는다. 슬랙, 컨플루언스, 이메일, 지라 이슈등을 사용해 최대한 정확히 밑그림을 파악한다.

다음 단계에서는 팀이 1주일에 얼마 만큼의 업무를 수행하는지 테이블로 표현한다. 마지막으로 작성한 테이블을 기반으로 미래나 현재의 프로젝트에 어떻게 사람을 할당해야 할지 판단한다.

참고

컨플루언스 업무 능력 계획 템플릿

https://www.atlassian.com/ko/team-playbook/plays/capacity-planning

교전 규칙(Rule of Engagement)

팀의 규칙들을 명문화 시키는 의식이다. 얼마나 세세하게 어디까지 정할 지는 팀별로 다르다. 먼저 원하는 팀의 방향에 대해서 간단히 토론을 한다. 다음과 같은 내용들을 공유 문서에 정리한다. 이어서 소개하는 ‘나의 사용 매뉴얼’과 함께 사용하면 더욱 효과가 있다.

  • 월요일은 회의 금지..
  • 코드 리뷰는 어떻게 진행해야 할지
  • 어떤 방식으로 피드백을 주고 받을지.
  • 팀내 공유 활성화 하기

참고

https://www.atlassian.com/team-playbook/plays/rules-of-engagement

나의 사용 매뉴얼

새로운 팀원이 팀에 들어오거나 하면 나의 사용 매뉴얼을 작성해 공유하는 동료들을 꽤 보았다. 자신의 업무 스타일과 과거 프로젝트 배경들을 아주 가벼운 분위기에서 동료들에게 설명하는 것이다. 이 설명서는 보통 새로 들어오는 팀원별로 아주 각양각생 이기 때문에 동료들에게 조금 더 허물없이 다가갈 수 있도록 도와준다.

첫 만남을 위해서만 사용하는 것은 아니고 매뉴얼을 작성해 블로그 형태로 게시해 두면 다른 팀 사람들도 더욱 쉽게 커뮤니케이션할 수 있을 것이다.

참고

컨틀루언스 나의 사용 매뉴얼 템플릿

블릿츠 (Blitz)

블릿츠(Blitz, 전격전) 는 말그대로 신속하게 문제를 찾아내기 위해서 진행하는 세션이다. 프로젝트나 기능 완성 후에 QA를 특별히 거치지 않는 상황에서 조금 더 자신감을 가지고 기능을 런칭하기 위해 여러 인원들이 모여서 동시에 사용자 시나리오를 재현하는 의식이다. 보통 드라이버가 블리츠 전에 여러가지 예상 가능한 시나리오들을 정리해 놓는데 참가자들은 해당 시나리오들을 따라가면서 이전에는 보지 못한 버그나 유의미한 피드백등을 발견하게 된다.

참고

https://www.mindsettlers.com/guide/5qzvTQqYh2eSYYAEmeOuEw

워게임 (Wargame)

워게임은 애자일의 의식은 아니지만 장애 시나리오를 미리 예상해보고 팀이 미리 대책을 세울 수 있도록 도와준다. 보통 워게임에 앞서서 1-2시간 정도의 브레인 스토밍 세션을 가진다. 세션에서는 시스템의 다이어그램을 크게 그려 놓고 예상되는 실패 모델과 실험들을 나열한다. 일반적으로 FEMA(Failure Model and Effect Analysis)를 사용한다. 가장 발생할 것 같은 우선순위가 높은 시나리오들을 먼저 실험하게 된다.

Renovate로 의존성 관리

MSA에서 의존성 관리

MSA에서 라이브러리 업데이트는 꼭 해야 하지만 잊기 쉬운 특성을 가진다. 이는 손씻기나 양치질등과 닮아 있다. 열심히 해도 티가 안난다. 문제가 생기기 전까진!

백엔드 개발자의 백미는 자동화를 통해 적은 리소스로 많은 사용자들을 대상으로 한 서비스에서 발생하는 문제를 해결하는데 있다. Renovate는 버전업 프로세스를 사용자가 원하는 만큼 자동화 시켜준다.

주기적인 버전업은 잠재적인 기술부채를 줄이고 서비스를 더욱 안정적으로 쓸 수 있게 해준다. 최근에는 보안 관련 패치가 버전 업그레이드를 통해 자주 일어난다.

때문에 주기적으로 라이브러리를 업데이트 하지 않으면 기술 부채를 조금씩 저축하는 것과 같다. 지금 팀에서는 매 빌드시마다 SourceClear 를 사용해 소스코드와 라이브러리의 취약점을 분석한다.

작성한 코드에서 취약점이 발견 되는 경우 직접 수정을 하면 되지만 Spring이나 Jackson과 같이 사용중인 라이브러리 취약점이 발생하면 라이브러이 버전 업그레이드로 필요하다. 문제는 이 취약점이 꽤나 빈번하게 발견된다는 점이다. SourceClear와 연동하고 나서 버전 업그레이드가 팀내의 잡일처럼 되어버렸다.

그와중에 Renovate는 가뭄에 단비같은 존재, 사용자 설정한 내용대로 버전 업그레이드를 위한 풀리퀘스트를 생성해준다. 자동으로 master머지도 가능하며 시간당 생성하는 풀리퀘스트의 수 등 아주 다양한 설정이 가능하다.

팀에서는 kotlin + spring + gradle + docker 플러그인을 사용하고 있는데 이외에도 아주 다양한 플러그인을 지원한다. 아래 스크린샷에서도 확인할 수 있듯이 change log까지 전부 첨부해준다. 이건 개발자가 PR을 생성할떄 잘 안해주는 부분인데, Renovate는 세심하다.

Github에서는 App으로 제공되고 있으며 Gitlab이나 기타 설치형 저장소에서는 Renovate Bot을 직접 호스팅 하는 것도 가능하다. 아래는 팀의 일부 저장소에서 사용중인 renovate.json 설정 파일이다. 원하는 만큼 세세하게 설정이 가능하다. https://docs.renovatebot.com/self-hosted-configuration/

</p>
{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "schedule": [
    "after 10am and before 5pm every weekday"
  ],
  "timezone": "Australia/Sydney",
  "prConcurrentLimit": 20,
  "prHourlyLimit": 1,
  "automerge": true,
  "dockerfile": {
    "enabled": true
  },
  "maven": {
    "enabled": true
  },
  "terraform": {
    "enabled": true
  },
  "extends": [
    "config:base"
  ]
}

<p>

Bitbucket에서 혼자서 열심히 버전 업중인 Renovate

Pull Request 템플릿

문제점

하지만 소프트웨어 문제가 언제나 그렇듯이, 항상 좋기만 한 건 없다. 그렇지 않아도 몇일전에 관련된 장애가 한 건 있었다. reactor-netty 에서 중대한 메모리 누수가 발견되서 부랴부랴 전부 롤백해야했다.

사실 Renovate의 문제라기 보다 시스템적으로 버전업시의 충격을 흡수 할 수 있는 쿠션이나 버퍼가 존재했어야 한다. 배포가 잦지 않다면 스테이징 서버가 이런 역할을 할 수 도 있겠지만 문제가 일어난 서비스는 배포가 아주 빈번하게 일어나서 버전 업그레이드가 문제인지 개발자가 머지한 PR이 문제인지 판단하기 힘들었다. 

그래서 우리는 모든 스프링기반 프로젝트의 베이스가 되는 프로젝트를  만들고 해당 프로젝트는 자동적으로 의존성을 갱신하고 배포되도록 했다. 

이글을 읽는 분들도 저장소에 Renovate를 적용해 보고 어느정도 자동화가 가능할지 테스트 해보길 권한다.

라인 증권 프로젝트 회고

한국에서는 크게 알려지지 않았지만 2019년에 라인 증권 서비스를 일본을 대상으로 공개했다. 라인에서 근무한 시간은 길지 않지만 운 좋게 파이낸셜 개발실에 소속되어 정보 벤더(블룸버그, 로이터등과 같은 회사)들과 내부시스템의 연동, 가격 결정 시스템 등 나름 핵심적인 업무들을 1년간 수행했다. 배운 것들을 정리해두려고 했지만, 국제이사에 이직까지 겹쳐서 하루 이틀 미루다가 더 늦기 전에 이곳에 옮겨 본다.

라인 증권에 대해서

2020년 상반기, 코로나바이러스로 실물 경제는 어려워지는데 젊은 사람들은 부동산 시장에서 놓친 기회를 만회해보려고 주식 투자를 늘리고 있다. 일본의 증권 업계도 일본인들의 장롱 속에 된 현금을 어떻게든 주식 시장으로  가지고 오고 싶어 한다. 그러기 위해서 일본 증권 업계가 해결해야 하는 문제들은 수도 없이 많지만, 그중에 라인 증권이 해결하고자 하는 문제는 다음과 같았다.

소액으로 투자가 가능

최근 주식을 시작한 사람들은 잘 모르겠지만 몇 년 전 까지만 해도 코스피는 10주 단위로만 거래가 가능했다. 예를 들어 액면 분할 전의 삼성전자를 사기 위해서는 1,000,000~ * 10, 적어도 천만 원 이상의 현금이 필요했다. (2015년 코스피는 증시 활성화를 위해 1주 단위 거래를 허용했다, 관련기사) 반면 닛케이의 거래 단위는 무려 100주. 닌텐도 주식이 한 주에 4만엔 정도 하니 닌텐도에 투자하기 위해선 400만 엔이 필요하다. 그래서인지 일본에서 주식에 투자하는 사람들은 여유돈을 몇억 씩 굴릴 수 있는 부자의 이미지가 강하다. 일본의 2, 30 대의 사회 초년생들이 주식을 할 수 있을 턱이 없다. 라인 증권은 이를 파고들어 젊은 사람들이 소액 투자가 가능하도록 하는 걸 목표로 했다.

편리한 UI

내가 UI/UX의 전문가는 아니지만 적어도 초심자를 대상으로는 라쿠텐(楽天)이나 다이와(大和)보다는 로빈후드가 제공하는 UI/UX가 몇 배는 사용하기 쉬워 보인다. 라인 증권은 기존 일본 증권사보다 사용하기 쉬운 UI와 손쉬운 거래 플로우를 제공하려고 했다.

로빈 후드

결과적으로 첫 번째 두 번째 모두 장외거래(Over The Counter)를 도입해 시스템을 밑바닥 부터 개발함으로서 해결이 가능했다. 일반적인 주식 거래는 증권 거래소를 통해 이뤄지고 거래 당사자들은 중개 수수료만 증권사에 지불하는 구조다. 장외 거래를 사용해 라인이 미리 선정한 주식들을 보유하고 고객들은 거래소를 통해 타인과 거래하는 대신 라인과 거래를 하게 되는데 그렇게 해서 100주 단위가 아니라 1주 단위 거래는 물론 정규 거래 시간을 지나서 야간에도 거래가 가능해지며 사자/팔자 주문의 흐름도 단순해진다.  더불어 라인도 주식을 보유하고 거래하는 리스크를 지니기 때문에 일반 거래소를 통한 거래 보다는 수수료가 더 붙는다. 이 수수료를 붙인 가격을 상황에 맞게 다양하게 계산해 리얼타임으로 가격을 생성하는 것으로  일본 거래소가 가지는 여러 가지 제약으로부터 풀려나는 것이 가능하다.

일반적인 증권회사의 시스템들이 한국 일본 모두 어느 정도 패키지화가 많이 되어 있어서 계좌 부분을 제외하고는 벤더의 기존 코드를 빠르게 재사용해 개발하는 것이 가능한데 이렇게 새로운 거래 방법을 도입하면 되면 추가적인 개발이 굉장히 많이 필요하다는 점이다. 라인이 이런 모델을 택하게 되면서 사실 개발 난이도도 높아지고 프로젝트가 실패할 가능성도 높아지게 된 것이다. 다음은 추가 개발이 필요했던 내용이다.

  • 라인 증권이 제시하는 팔자/주문 가격 생성
  • 라인이 손해를 보지 않게 하기 위해 다양한 거래 검증 로직 구축 (자세한 내용은 영업비밀)
  • 라인이 주식을 보유할 수 있도록 재고 관리 및 구매 시스템 구축
  • 등등등..

라인 증권의 아키텍쳐 방향성

나는 지금도 그렇지만 절대적으로 이 정도 규모의 새로운 프로젝트는 MSA로 가야 하며 2~3명으로 나누어진 도메인 팀끼리는 API로 대화하며 도메인 개발을 진행하길 원했다.  내가 경험한 모노리스 서비스는 git 커맨드 조차도 쉽게 실행되지 않고 로컬에서 서버를 구동하기 위해선 8기가 이상의 메모리가 필요했으며 제일 중요하게는 배포가 제한적으로 일어날 수밖에 없다는 단점이 있었기 때문에 나름대로 계속 MSA를 주장했다.

그러다 결국은 나와 한두 명의 개발자 vs 나머지 개발자들의 구도가 되었고 어떻게 보면 굉장히 소외감을 느낄 수도 있는 상황이었다. 하지만 결과적으로는 다른 팀보다 훨씬 적은 인원으로 6개월 동안 3개 정도의 서브 시스템을 구현하는 데 성공했다. 단순 곁다리 시스템이 아니라 가격 생성/검증부터 외부 정보 벤더 연결 등 핵심적인 부분이었으며  내가 생각하기에 프로젝트 비용 중에 가장 가성비가 잘 나왔던 영역이라고 생각한다. 오히려 인원이 적어서 멤버들끼리 지식 공유가 잘되었고 의견 충돌도 훨씬 적었던 것 같다. 물론 우리 팀의 개발자들은 모두 한국, 중국, 한국계 캐나다인 등 이었고 나머지 전부는 일본인 팀이었다.  나는 최대한 문서작업을 줄이고 비즈니스 로직은 코드로 표현하는 이상적인 환경을 원했기 때문에 문서 작성을 게을리했지만, 일본인으로 구성된 팀은 문서화를 정말 철저히 했으며 나도 그에 따르기를 원했다. 그래서 프로젝트의 후반에는 많은 시간을 문서작성에 사용한 것 같다.

라인 증권 개발언어

개발 언어는 스프링 기반의 자바를 주로 사용했는데 나는 WebFlux의 도입을 적극적으로 이끌었던 반면 일본인 팀은 사용이유에 대해서 잘 이해하지 못했고 본인들이 기존에 사용해서 익숙한 Spring Web을 사용하길 원했다. 기술 선택에 있어서 익숙함이 그것을 선택하는 유일한 이유가 되어서는 안된다고 생각했고 다른 팀원들이 따라와 주길 바랬는데 사실 그들도 시니어였기 때문에 몇번 충돌이 일어났었다. 프로젝트 진행시에 가끔 본질적인 업무가 아닌 부가적이라고 생각하는 부분에서 시간을 더 잡아먹거나 해결되지 않아서 답답함을 느끼거나 짜증을 내곤한다. 그 당시에는 팀 내 커뮤니케이션을 부가적인 업무라고 생각해 설득에 소홀했던 것 같다. 지금 와서 생각해보면 과연 우선순위가 떨어지는 문제였을까?  팀내에 다수의 사람이 이해하지 못한 상황에서 WebFlux를 그대로 사용했던 결정은 옳았던 걸까? 성공적으로 출시는 가능했지만, 다시 개발하게 된다면 그 부분은 조금 고쳐보고 싶다. 개발자는 코드만 보겠다는 자기 암시에 빠지기 쉬운데 경력이 10년이 넘어가면서부터 팀 내의 소통이야말로 높은 수준의 코드작성 만큼 우선순위를 높게 두어야 할 문제임을 느낀다.

WebFlux를 사용하길 원한 이유는 실시간 가격변동이 푸시 서비스를 통해 이루어지는데  푸시 서비스를 대상으로 리액티브 프로그래밍을 사용하면 동시성 제어가 더 쉬워지고 가독성 좋은 코드를 작성할 수 있다고 생각했기 때문이다. 구현한 코드를 CompletableFutre 등과 같이 Java가 제공하는 비동기 기능만을 가지고 작성했다면 훨씬 이해하기 힘들고 테스트하기도 힘들었을 것이다. 실제 WebFlux의 많은 예제가 증권 거래 시스템을 다루고 있는 점은  기술셋과 도메인이 그만큼 잘 맞는다는 방증이다. 더불어 증권은 특정 시점이 되면 돌아가야 하는 배치 작업이 많았기 때문에 WebFlux를 사용해 작업을 겹치지 않는 부분 작업으로 분리해 동시성을 높일 수 있었다.

라인 증권 개발/배포 인프라

인프라의 경우 클라우드는 전혀 고려하지 못했고 레디스, 엘라스틱 서치, 카프라, MySQL등 을 다양하게 사용했다. 금융 서비스라서 외부에 뭔가 안정적인 느낌을 주기위해서 MySQL을 사용했을 뿐 계좌 시스템을 제외한 증권 서비스는 레디스, 엘라스틱 서치만 가지고도 안정적으로 운용하는 것이 가능하다고 본다. 라인은 자체적으로 아주 대용량의 레디스 클러스터를 운용하고 있어서 노하우도 풍부하고 Lettuce 가 제공하는 리액티브 연동도 아주 마음에 들었다. 하지만 그만큼 잘 운영하기 위해서는 여러 번의 시행착오가 필요했으며 이곳에 사례들을 열거하지 않겠지만 증권 조직 자체에서 SPOF가 될 수 있는 부분을 찾아 이중화하고 에러가 발생해도 쉽게 복구할 수 있도록 시스템을 구성하자는 분위기가 있었기 때문에 많은 좋은 사례들을 발견할 수 있었다.

많은 일본인 개발자들은 쿠버네티스나 도커등을 적극적으로 활용하기를 원했지만 나는 배포가 자주 일어날 수 없는 금융 환경에서 해당 시스템들을 굳이 도입해야 할 이유가 없다고 생각했다. 배포를 위해서는 각 사업부서의 허가를 받는 시스템이 있었는데 배포해야 할 내용을 쫙 늘어놓고 사업 부서에서 배포하는 목적과 리스크 등을 평가하는 방식이었다. 사업 부서의 의견은 중요하고 피드백 루프에 포함되는 것이 필수적이지만 전체적인 배포에 관련한 프로세스는 굉장히 쓸모없는 절차라는 생각이 들었다. 왜냐하면 사업 에서 각 배포의 리스크를 이해하기 쉽지 않기 때문이었다. 결국 시스템의 완결성을 책임지는 조직은 개발팀인데 라인 내부에서는 PM, QA, 개발조직으로 권한을 나눠주는, 내가 보기엔 이상적이지 않은 암묵적인 규칙이 존재했으며 특히 증권 에서는 노무라 출신의 사업 조직까지 겹쳐지며 더욱 혼란스러운 구조가 되었다. (사실 이런 삼권분립과 유사한 이상한 구조는 일본과 한국으로 나눠진 라인 내부의 사정도 기인하는 것 같다)

기타외부 요인

노무라 증권 사람들하고 일해본 것은 굉장히 재미있는 경험이었다. 노무라 증권은 일본 내에서는 최고의 직장으로 꼽히며 직원들도 엘리트라는 인식이 있지만, 역시 오래된 회사답게 웹 서비스 관련해서는 굉장히 뒤쳐져 있었다. 하지만 주식 관련 업무에 관해선 정말 노련하고 능숙한 사람들이 많았으며 기술 부분 쪽 사람과는 다르게 뭔가 시원시원 하기도하고 일본인 답지 않게 의견을 바로바로 개진하는 점도 좋았다. 다만 내가 원하는 서비스 개발 방향과 맞지 않는 의견들을 자주 이야기해 내 매니저는 노무라 사람들로 부터 불만을 들어야 했지만 다행히 끝까지 나를 신뢰해 주었기에 때문에 나와 팀원들은 개발에 집중해 프로젝트를 무사히 마무리할 수 있었다.

사업부분과 가장 대표적인 의견 차이는 정보 벤더 선택시에 있었는데, 나는 당연히 해외 벤더들을 선호했다. 그 이유는 모든 문서가 영어로 잘 되어있고, 높은 수준의 SDK를 보유했으며 회사 자체의 기술력도 훨씬 높은 축에 속했기 때문이다. 그러나 일본 쪽에서는 일본 출신의 벤더들을 선호했고 나를 설득시키기 위해서 굉장히 노력했는데, 나는 일본 회사는 API가 잘 정리되어 있지 않고 개발환경조차 존재하지 않고 심지어 API 문서를 요구하면 사전 두께로 프린트해서 보내주는 회사였다.  나는 일본 벤더를 선택하면 프로젝트 일정이 늦어질 것이다 라고 이야기 했다. 다행히도 나의 반 허풍은 먹혀서 내가 원하는 외국계 벤더가 될 수 있었지만, 릴리즈하고 나서도 집요하게 벤더 교체에 대한 요구를 지속 했다. 사업쪽에서는 벤더의 서포트 조직이 외국에 있는 사실을 엄청나게 못미더워 했고 벤더 쪽에서 개발자 입장에서 이해할 수 있는 작은 실수라고 발견하면 엄청나게 쏘아붙이기 일쑤였는데 보는 내가 안타까워질 정도였다.

해당 정보 벤더 사하고 일하게 되면서 정말 몇억을 줘야 써볼 수 있는 여러 가지 시스템들을 다뤄 보았으며 나름 재미있었다. 장의 움직임을 그대로 재현할 수 있는 도구, 전 세계 모든 회사의 기업 평가 정보를 제공하는 시스템등 관련 업무를 하지 않으면 존재자체를 모를 도구들이 있었으며 그쪽 분야에서 또다른 사업 기회가 있지 않을까 생각도 들었다.

마무리

이것으로 대충 정리를 마친다. 사실 라인에 입사하고 2년도 안 돼서 뛰쳐나오긴 했지만,  매니저의 배려와 운이 적절하게 맞아떨어져서 정말 재미있는 시스템 개발을 해볼 수 있었다.  (1년만에  증권 시스템을 개발하고 출시하는 경험은 흔치 않다)  아쉬운 점으로는 군 생활을 전역 후에 돌아보는 것과 유사하게 프로젝트에 대한 기억이 좋은 추억 위주로 남아서 가끔 팀원들에게 조금 더 협조적으로 둥글둥글하게 대할걸 하는 아쉬움이 든다.  인생 선배들이 자주 하는 조언중에 “Don’t burn your bridges”가 마음에 와닿는데, 회사내에서는 티격태격 주도권을 위해 다투지만 결국 이직하고 나면 같은 업계 사람일 뿐이다. 채용 기준이 무엇 이냐는 질문을 받았을 때 “인상”이라고 대답했던 매니져의 말을 당시에는 농담으로 받아들였지만, 업계 20년 이상의 경력에서 나오는 6감, 직관력등 그 능력을 이제는 인정해 줄만 하다. 내가 부족한 언어 능력으로 인해 독불장군처럼 고집을 굽히지 않았을 때 철저히 위임하고 맡겨준 그 판단이 맞았다. 한국,일본,미국, 호주등 다양한 곳에서 개발을 했지만 개인적으로는 라인 증권을 개발할 때 일적으로는 제일 재미있었다. 일본의 개발 문화와 비교해 보면 아이러니하다고 생각하는데 사용하는 기술셋들의 화려함이 아니라 본인이 스스로 느끼는 프로젝트 기여도 몰입도 등이 만족도에 큰 영향을 미치는 것 같다.  사실 네이버, 카카오 같은 조직에서도 새로운 서비스를 개발하고 출시하고 성장하는 모습을 처음 부터 보는 사람들은 몇 안되고 그럴 수 있는 사람들은 굉장히 운이 좋은 축에 속한다. 나는 라인 증권에서 느낄 수 있는 익숙함을 뒤로하고 아예 도메인이 바뀌어 버렸지만 가끔은 그곳에서 집중하면서 개발했던 시절이 그립다.

곧 라인 증권의 거래소 서비스가 오픈한다는 소식을 들었는데 매니저와 팀원들에게 노력의 열매가 모두 골고루 돌아가길 바란다.

onErrorDropped explained

RxJS merge operator

웹플럭스나 리액티브로 서비스를 개발하고 있다면 로그에서 onErrorDropped 메시지를 보게될 확률이 높다.  리액티브 애플리케이션을 사용하는 주된 이유는 높은 동시성을 달성하기 위한 것인데 동시성이 높아져 비동기로 여러 장소에서 데이터를 수신하는 경우 해당 에러가 자주 발생한다.

onErrorDropped가 발생 이유

이 에러 메시지는  정상적인 경우라면 오류가 발생했을 때 다운스트림으로 onError 에러를 전달해야 하지만 이미 다른 스레드에서 onError가 발생해 전체 스트림이 terminated된 상황임을 알려주는 것이다. 이미 종료된 스트림에 onError를 전달하는 행동은 리액티브 사양을 위반한다. 이 사양 링크 7번째 항목을 참고하자.

모든 것이 동기화된 세상에서는 이런 비정상적인 에러를 일어나지 않는다. Operators.onErrorDropped가 사용된 곳을 라이브러리에서 검색해보면 flatMap, merge와 같이 순서가 없이 실행되는 연산자들에서 주로 사용되고 업스트림의 순서를 보장하는 map이나 concat에서는 사용되지 않는다.

스프링 부트 애플리케이션이 WebClient를 통해 여러 데이터 세트를 동시에 가져온다면 발생한 에러는 이미 진행중인 다른 요청들에 영향을 줄 수 있다.

대처방법

onErrorDropped 로그가 너무 많다면 진짜 중요한 에러를 숨기고 있을 가느성이 높다. onErroDropped의 로그 레벨을 낮추는 몇가지 방법이 존재한다. (Project Reactor 코어에서 버전 업그레이드를 통해 조정될 가능성도 있다)

Hook을 사용

onErrorDropped 의 Hook을 설정하면 onErrorDropped가 발생했을 때 Hook에서 지정한 람다 함수가 실행된다. 프로젝트 리액터의 스케쥴러와 마찬가지로 Hook은 글로벌로 등록된다.

Hooks.onErrorDropped { log.info("onError Dropped.", it) }

onErrorContinue사용

동시성이 발생할 수 있는 기존 연산자를 확장해 각 Source 퍼블리셔들에 onErrorContinue를 추가해준다. 이 방법은 위 방법보다는 영향이 덜하며(less intrusive) 원하는 곳에만 적용이 가능하다. 다만 onErrorContinue의 사용법이 다소 복잡해 특정 연산만을 대상으로 작동함으로 메뉴얼을 잘 읽어봐야 한다. 아래는 merge연산자를 확장해

fun merge(sources: Iterable<Mono<T>>): Flux<T> = Flux.merge( 
    sources.map { 
        it.onErrorContinue { throwable, any -> log.info("Suppressed error in merging flux, {}", any, throwable)} 
}
)