본문 바로가기

리뷰/컨퍼런스, 세미나 등 리뷰

[컨퍼런스] if(kakao)dev2022 (2022/12/07~12/09)

728x90

카카오에서 작년(2022년) 말에 진행한 개발자 컨퍼런스를 보고 후기를 작성하고자 한다. 한 달도 더 지나서야 듣게 됐는데, 이거 말고 NHN Forward 2022도 아직 못 들었다. 우선 카카오 컨퍼런스를 듣고 여유가 되면 들어봐야겠다.

if(kakao)dev2022

 

if(kakao)dev2022

함께 나아가는 더 나은 세상

if.kakao.com

카카오 컨퍼런스는 작년과 제작년 컨퍼런스는 듣지 못했던 것 같다. 이번에 처음 듣는데 세션 수가 어마어마하게 많아서 놀랐다.

 

세션별로 듣고 싶은 내용들이 더 많기는 했는데, 워낙 많아서 다 듣기에는 시간이 너무 오래 걸릴 것 같다. 그래서 우선적으로 듣고 싶으면서 당장 도움이 될 것 같은 세션들을 위주로 봤다. 세션별로 퀄리티도 좋고 만족하는 컨퍼런스였다.

 

아래는 늘 그렇듯 각 세션별로 정리한 내용이다. 링크에 발표자료들까지 다 올려주셨으니 같이 확인해보는 것도 좋을 것 같다.


API 테스트 주도 개발의 시작

카카오 사내 대상으로 테스트 플랫폼 개발을 담당하는 분이 나와서 발표를 진행한다. 이 소속 자체가 되게 신기했다. 테스트 플랫폼을 전담해서 개발하는 직원이 있다는 게 놀라웠다.

발표는 API 테스트 자동화에 대해 설명한 뒤, API 테스트 자동화를 어떻게 지원하고 있는지 소개한다. 마지막으로, API 테스트를 통해 기획, 개발, 운영까지 가능한 API 테스트 주도 개발 프로세스를 소개한다.

보통 API에 대해 얘기할 때 개발자는 RestfulAPI를 먼저 생각할 것이다. 이번 발표도 RestfulAPI로 한정지어서 진행한다.

 

API 테스트는 비즈니스 레이어에서 수행된다.

API 테스트는 시스템이 제공하는 API가 기능, 성능, 보안, 신뢰에 대한 기대치를 충족하는지 확인하는 작업을 말한다. UI를 통해 테스트를 할 때 보다 더 빠르고 정확하다. UI 테스트로는 화면의 Look and feel에 초점을 맞춰 테스트를 하고, API 테스트로는 비즈니스 레이어를 테스트하는 것이 효율적이다.

  • UI가 없더라도 기능 테스트 가능
  • UI 테스트에 비해 테스트 작성이 간단
  • UI 테스트 전에 사소한 버그 발견 가능

결국, 테스트 비용을 줄일 수 있다. 

 

API 테스트 vs 단위 테스트

단위 테스트는 타깃의 기본 기능들만 테스트가 가능하다. xUnit을 통해 메소드 테스트 같은 것을 예로 들 수 있다. API 테스트도 타겟의 기본 기능만 테스트할 수 있다. 다만, API 테스트는 추가적으로 시스템 간의 통합, 보안, 성능 등 추가적 이슈들을 쉽게 확인할 수 있다.

모든 테스트는 상호 보완적이기 때문에 각 테스트(UI/Unit/API Test)는 목적에 맞게 적절한 시기와 알맞은 단계에서 수행되어야 한다.

 

API 테스트 자동화는 CI/CD 단계에 포함되어 있어야 한다. 빌드 후 API 테스트를 실행시킴으로써 간단하게 테스트 자동화를 할 수 있다. API 테스트를 CI/CD에 통합시키기 위해서는 API 테스트를 개발 단계에서 리그레션 테스트로 만들어야 한다.

 

이후에는 Test LAB이라는 카카오 테스트 플랫폼에 대한 소개를 한다. 시나리오에 따라 Test Suite를 어떻게 조작이 가능하고, 각 테스트 케이스는 어떻게 구성이 가능한지에 대한 설명이다. 하지만, 툴은 어차피 사내 대상으로 사용하고 있는 것이라 찾을 수 없다. 다른 API 테스트 자동화 툴과 유사할 것으로 예상돼, 다른 툴을 사용할 때 어떤 방식으로 다양하게 활용할 수 있을지 생각하는 정도로만 봤다.

 

API Test Driven

API Test Driven은 Test LAB의 개발 방향성이라 할 수 있다. API 테스트 자동화를 넘서 애플리케이션 개발을 동료와 함께 API 테스트로 시작하는 프로세스다. TDD랑 확연히 다른 점은, 소스코드 단계에서의 개발 방법론이 아니라 API 단계에서의 개발 방법론이라는 것이다.

 

API Test Driven 순서

API Test Driven은 TDD와 같이 순서가 매우 단순하다. 설계에서 API 설계서를 작성하고, 이후 단계에서는 API 기대치에 맞게 테스트를 성공시키기만 하면 된다. TDD와 같이 먼저 개발팀은 클라이언트와 서버 간의 통신을 실패하는 API 테스트 작성을 통해 설계한다. 애플리케이션 요구사항에 맞게 메서드와 요청 바디 등을 넣고 예상되는 상태 코드와 응답 바디 등을 검증한다. API 테스트는 요구사항 스펙 정의와 같기 때문에 이를 통해 문서를 대체할 수 있다.

구현 단계에서는 미리 작성된 API 테스트를 통해 협업을 진행한다. 개발주기에 맞춰 Mock 서버를 시행시키고, 클라이언트 개발자는 Mock 서버를 통해 클라이언트 개발을, API 개발자는 테스트 통과 여부를 수시로 확인할 수 있다. 

빌드 단계 이후에는 API 테스트 자동화가 필요하다. CI/CD에서 빌드 이후 API 테스트를 자동화해, 배포 진행 여부를 판단할 수 있다. 추가로 성능 테스트, 보안 테스트 등을 배포 전에 진행한다.

운영 단계에서는 API 테스트 스케줄링을 통해 모니터링한다. 올바른 상태값이 오는지, 지연이 있지는 않은지 등을 운영 단계에서 모니터링해야 한다.

 

API Test Driven을 하게 되면, 클라이언트가 필요한 기능과 서버가 제공하는 기능이 모두 API 테스트로 작성돼, 추가적인 설명이나 정의서가 필요 없다. API 테스트로 Mock 서버와 문서화를 할 수 있어 커뮤니케이션 및 개발 비용을 줄일 수 있다. 기획 단계에서부터 테스트가 작성되니 생산성이 향상되고, 배포 이후 운영 단계에서는 제품 품질 보증이 쉽다.


카카오톡 메시징 지표 이상 감지 시스템의 개선사례

카카오톡 메시지를 전달하는 기능을 담당하는 분이 나와서 발표를 진행해 주신다. 카카오톡 말풍선 좋아요 기능이 동작하지 않았던 이슈를 예로 설명해 주신다.

 

해당 이슈는 다행히 빠르게 확인이 되었지만, 빠른 복구도 필요한 상황이었다. 장애의 원인을 파악하기 위해 시스템 구조, 최근 배포나 커밋 로그, 릴리즈 노트 등을 살펴봤고, 실제 서버나 네트워크 장비에 장애나 작업이 있었는지도 확인했다. 정확히 원인을 파악하지 못해 여러 의심되는 지표를 보면서 추론했다. 이때, 서비스의 이해도나 경험에 따라 소요시간에 큰 차이가 발생할 수밖에 없다.

결국, 원인은 비즈니스 변경으로 해당 API 호출 증가로 MongoDB 조회 쿼리가 증가한 것이 문제였다. 응답이 느려져 문제가 발생한 것으로, 쿼리가 몰리지 않도록 수정해 해결했다. 빠른 복구 이후에는 방어코드나 테스트 코드를 추가하고 관련 지표에 알림을 추가하거나 조정을 하게 된다.

 

장애 대응 프로세스

카카오톡에서 이상이 발생했을 때 정확하게 어느 부분이 문제인지 빠르게 알려주기 위해 최근에 개선하고 있는 구조에 대해 공유를 해준다. 12년 동안 카카오톡에는 다양한 기능이 추가됐고, 서버나 컴포넌트가 증가할 수밖에 없었다. 많은 트래픽과 여러 서버 간 호출하는 API도 복잡해졌다.

 

카카오 트래픽

위 사진과 같이 일 평균 수발신량이 100억 개이며, 일 평균 로그만 해도 6 테라다. 또, 기능 추가와 디버깅 정보가 많아져 더 증가하는 추세다. 이렇게 증가하는 로그에서 API 응답 지연, 에러, DB 인프라 문제 등을 파악하게 된다.

인프라나 DB 팀에서도 모니터링을 하지만, 메시징 팀에서는 원인을 빠르게 파악하고 지표를 한눈에 보기 위해 되도록 많은 지표와 정보를 직접 수집한다. 메시징 애플리케이션 로그 수집은 ELK를 사용하고, 안정적 수집을 위해 메시지 큐로는 Kafka를 사용한다. 지표를 그래프로 그릴 때는 Grafana를 사용하며, 이상 감지 시 알림 시스템은 여러 채널로 보내고 있다. 이렇게 나온 지표는 수천 개가 된다.

 

지표를 찾아내는 전통적인 임계치 기반의 분석이 아닌, 문제와 알림이 직접 찾아오도록 구축을 고민했다.

 

시계열 데이터 모델을 이용해 이상탐지하는 방식으로 개선을 시작했다. 시계열 데이터의 이상탐지 모델은 데이터를 정제하는 전처리 작업이 쉬워, 이미지나 텍스트 관련 학습에 비해 단순하고 데이터 사이즈가 작아 별도 인프라 구축이 필요하지 않다. 비교적 빨라 GPU를 쓰지 않고도 빠른 구현이 가능했다.

 

학습 전에 데이터 특징 파악이 필요한데, 데이터의 추세성, 계절성, 순환성, 불규칙성 등에 대한 특징이 필요하다. 모델은 시계열 데이터 분석 관련 학습 모델이나 알고리즘이 많이 발표되고 있어, 처음에는 속도가 빠른 Prophet을 적용했다. 이후 여러 모델을 함께 돌릴 수 있도록 구축했다.

 

학습은 개발자의 개입 없이 이뤄졌다. Grafana로 데이터를 전달할 때, 중간에서 가로채 데이터를 자동으로 수집 및 학습했다.

 

현재 개선 중인 내용이라고 한다. ML/DL 관련해서 전반적인 지식이 있어야 듣기 좋을 것 같다. 이상 감지 시스템 도입으로 어떤 효과를 봤는지가 궁금했는데 아직 완전 적용 중인 것이라 해당 내용이 없다. 그래서 아쉬움이 있는데 일 평균 로그가 6 테라라는 건 정말 놀라운 것 같다. 회사에서 대용량 트래픽을 다루던 수준과는 차원이 다르다.


카카오톡 메시징 시스템 재건축 이야기

부제가 'C++ 서버를 Java 및 Kotlin으로'다.

약 4년에 걸쳐 진행 중인 메시징 시스템 재건축에 대한 이야기를 해준다. 이 세션이 가장 눈길을 끌었고 관심이 갔다. 그래서 아래 정리 내용도 많다.

 

카카오 하루 트래픽

2011년 '겁나 빠른 황소 프로젝트'를 통해 대대적인 시스템 개선 이후 이러한 트래픽을 받을 수 있게 됐다. 기존 루비로 이루어진 네스티드 시스템을 C++과 Java로 바꾸고, 당시 모바일 환경에서 빠른 통신이 어려웠던 Http 대신 경량화된 패킷을 통해 빠른 통신이 가능한 자체 프로토콜을 개발했었다. 그 결과 약 11년이 넘는 세월 동안 안정적인 서비스가 가능했다.

 

기존 메시징 백엔드

  • 설정 서버
  • 릴레이 서버 : 메시지 수발신을 위한 풀 메시 구조
  • 미디어 캐시 : 사진과 동영상 전송을 위함
  • 비즈니스 서버 : 메시지 저장과 채팅방 관리 

클라이언트와 직접 통신하는 앞단 서버는 대량의 트래픽 관리를 위해 C++로 구현되었고, 비즈니스 로직과 여러 DB 통신이 필요한 비즈니스 서버들은 Java로 구현했다.

 

C++ 백엔드 서버 애플리케이션은 epoll 기반 비동기 입출력을 수행한다. 잦은 메모리 할당으로 발생하는 시스템 콜로 인한 지연을 줄이기 위해 스레드별로 미리 메모리 버퍼를 할당해 사용한다. 이런 구조로 백엔드 서버 애플리케이션은 대당 50만 개 이상의 세션을 관리할 수 있게 됐다.

 

10년 동안 잘 동작하던 구조를 바꾸려고 하는 이유로는 앞으로의 문제가 남아있기 때문이다. 잘 튜닝된 커스텀으로 극한의 성능을 이끌어냈지만 계속 성능을 잘 내주기 위해서는 지속적인 유지보수가 필요하다. 하지만 시간이 지나 유지보수하는 사람이 교체된다면 더이상 유지보수하기 어려워진다.

 

앞단 C++로 구현한 서버들이 그랬다. 직접 개발한 프로토콜과 고성능을 위해 모든 부분을 직접 구현했다. 중속성을 줄이기 위해 외부 라이브러리를 최소한으로 사용했다. 불가피한 경우에는 static 라이브러리로 빌드해 아예 내재화시켰다. 이는 오히려 유지보수와 기능 개선에 제약사항이 되었다.

  • 자체 프로토콜과 내부 애플리케이션 로직들 간의 간결함으로 인해, 프로토콜 확장에 제약
  • 수많은 매크로와 최적화 코드로 인해, 나쁜 가독성
  • 라이브러리 내재화 및 제약적인 외부 라이브러리 사용으로 인해, 확장성 제약
  • 미리 할당된 메모리 버퍼풀로 인해, 성능은 좋아졌지만 해당 버퍼 크기로 인해 메모리 사용 효율이 줄어드는 문제
  • 안정적으로 동작은 하지만 메모리 관련 버그가 발생할 때마다 원인 파악이 점점 더 어려워지고 문제 해결 시간이 늘어남
  • 단위 테스트(gtest)를 사용했지만, 코드 규모 대비 매우 적음
  • 통합 테스트는 많은 시나리오를 커버했지만, 파이썬 스크립트를 통해 원격 테스트가 가능하며 개인이 테스트하기 위한 스테이지 구성은 어려운 상황
  • 배포 시스템은 쉘 스크립트와 파이썬 기반으로 실수가 발생하기 쉬운 구조
  • 인적 리소스 불균형 발생(C++ 기반 개발자 수급이 원활하지 않음)

 

유지보수 측면으로 본다면 배포 시스템을 개선하거나 유닛 테스트를 추가하는 방식으로 코드 안정성을 높이고 진입장벽을 낮추는 방법도 있다. 실제로 현재 재건축과 별개로 진행되고 있기도 하다.

현재 코드의 근본적 구조를 바꾸기 어렵기 때문에 동일 비즈니스 서버와 함께 가기로 결정했다. 이게 재건축 프로젝트의 1차적 목표가 되었다.

 

앞단과 마찬가지로 비즈니스 서버들도 10년동안 발전해 왔다.

비즈니스 서버

앞으로 이런 비즈니스 서버들의 모습을 담으면서 각 모델에서 필요로 하는 사항들을 기반으로 변형해 가기로 결정했다.

 

재건축 프로젝트는 크게 2 스텝으로 나눌 수 있다.

 

재건축 1차 대상

1차적으로 트래픽이 상대적으로 적거나 장애 영향도가 비교적 낮은 서비스부터 진행했다. 서버 모듈들의 역할은 명확하기 때문에 각 모듈을 일대일 대응 가능한 대체 서버를 구현하는 방식으로 프로젝트를 진행했다.
이중 구조 변화가 컸던 Session info repository와 private api server를 설명한다.

 

사내 서비스를 위한 API 서버는 호출단에서 자체 프로토콜을 사용했는데, 사내의 다양한 서비스에서도 프로토콜을 맞춰야 하기 때문에 확장성에 있어 제약이 있었다. 또, 문서화도 부족한 상황이었다.

이를 해결하기 위해 Http를 지원하기로 했고, 아스키독을 이용해 자동적으로 문서를 만들 수 있도록 했다. 추가적으로 쿠버네티스와 아르고를 이용해 유연한 배포가 가능하도록 했다.

교체하는 방식은 기존 PAPI 서버 앞에서 단순 프로토콜 변환만 수행하는 프록시 형태로 구현한 뒤, 완전히 대체하도록 도입했다. 

PAPI 서버 교체 방식

 

다음은 세션 정보 저장 서버로, 현재 클라이언트들이 어떤 서버에 접속해 있는지 등에 대한 정보를 저장하는 서버다. In-memory 저장으로 특정 기준으로 모듈러해 여러 서버에 저장하는 형식이었다. 이로 인해 확장이 어렵고 배포가 어려워 클라이언트의 추가 정보 또는 부가 로직이 들어가기 어려운 구조였다.

이 문제를 해결하기 위해 저장소를 애플리케이션 서버에 저장하는 것이 아닌, Redis에 분리 저장할 수 있도록 변경했다. PAPI 서버처럼 유연한 배포와 수평적 확장이 가능하도록 쿠버네티스 적용도 함께 진행됐다. 저장해야 할 세션 정보를 유연하게 확장 가능해졌고, 복잡한 정보들도 캐싱할 수 있게 됐다.

 

이렇게 재건축을 통해 현재 설정서버, 세션정보서버, PAPI 서버는 JVM 기반의 새로운 서버로 변경되어 안정적으로 서비스 중이다.

 

아래는 재건축 2차에 대한 내용이다. 비교적 트래픽이 적고 덜 민감했던 서비스들을 변경할 차례다. 메시지 릴레이와 클라이언트를 관리하는 서버군(Relay & Session manager)이 그것이다. 해당 서버는 풀 메쉬 형태로 구성되어 대당 50만 개의 세션을, 최대 100k 정도의 트래픽을 처리하는 고성능 애플리케이션이다.

 

해당 서버를 JVM 기반 언어로 포팅할 때 기존 서버들 대비 좀 더 조심스러웠던 점들이 있다. JVM 언어 적용 시 빠르고 안정적 메시지 처리에 큰 걸림돌이 되었던 것은 가비지 컬렉션이 발생하는 스탑 더 월드 현상이다. 이를 보완하기 위해 ZGC를 이용했다.

기존 프로젝트에서 모델과 테스트에서만 제한적으로 사용했던 코틀린을 전면 사용하기로 했다. 이로 인해 기존 프로젝트 대비 간결하고 가독성 높은 코드를 가지고 빠르게 개발 가능했다. 현재 스레드 풀을 이용해 비동기 릴레이를 하지만, 코틀린에서 제공 중인 코루틴을 적용 예정인 점도 주된 이유 중 하나다.

적용 방식은 클라이언트와 풀 메쉬 상대 서버에서는 해당 모듈이 신규 모듈인지 아닌지를 모르도록 블랙박스 형태로 진행했고, 이를 통해 한 대씩 순차적으로 적용해 장애 위험도를 낮추었다.

 

마지막으로, 제일 큰 고민으로 기존 C++ 서버 대비 성능이 잘 나오는 지다.

성능 개선 정도

새롭게 로그인할 때, 메모리 할당이 활발할 때, JVM 애플리케이션의 CPU 사용량이 높았다. 하지만, 실서버 자원에 비해 CPU 사용량이 낮아, 이 정도 증가폭은 큰 영향이 없을 것이라 판단했다.

로그인 이후에는 C++ 서버와 크게 다르지 않았다. 프로토콜 처리 속도나 메시지 릴레이 시간은 내부 로직 개선을 통해 일부 개선되었다.

스탑 더 월드 현상은 G1GC 사용 시 최대 170ms 이하로 나왔으나, ZGC에서는 1ms 이하로 나왔다.

 

추가적으로 개선 진행하는 내용으로 세션 클래스 구조 개편으로 멀티 디바이스를 고려할 수 있도록 했다. 또한, 클라이언트 요청에 들어오는 데이터 스트림 중 불필요한 필드는 파싱하지 않도록 고려했다. 각 세션에서 동시성 영향을 받는 데이터와 테이블의 락 구조도 스펙에 맞게 개선 중이다. 메시지 릴레이를 비동기 처리하도록 했다. 비동기 릴레이는 단순히 스레드 풀 Task Executor를 통해 이루어지고 있지만 CPU 자원을 좀 더 줄이기 위해 Coroutine을 도입할 예정이다.

 

2차는 2023년 중에 완료될 것으로 보인다.

변경사항 요약
개선사항 요약

코틀린 언어를 잘 모르지만 코드 수는 4만에서 2만 라인으로 줄이고, 가독성도 확보하면서 확장성 및 유지보수성을 모두 갖게 된 것 같다. 우리 회사도 안 그래도 C++ 개발자 모집에서 어려움이 있어 보이는데 어떻게 변화할지 궁금하다.


카카오 계정 캐시 전환기

카카오 계정에는 프로필, 연락처, 배송지 등과 같은 정보가 있다. 카카오에서는 DB 앞단에 캐시 레이어를 두어 부하를 분산하고 있다. 여기서는 이 캐시 레이어를 전환하는 내용을 설명한다.

50만 트래픽을 처리하는 캐시 레이어

 

전환하게 된 원인은 2020년 11월 CentOS 6의 EOL이다. 여기서 캐시 서버들이 OS 버전업이 필요해졌다.

기존 캐시 시스템은 Arcus라는 오픈소스를 사용하고 있었다. 시스템을 교체할 때 Arcus를 유지할지에 대해 고민했다. 잘 동작하는 솔루션이지만, 몇 가지 문제가 있었다.

  • 대중적이지 못한 기술로 러닝 커브가 존재
  • 기술 지원이 어려움

어차피 재구축해야 한다면 시스템을 교체하면서 Redis를 도입하기로 했다.

 

Arcus가 캐시 클러스터이기 때문에, Redis 클러스터로 전환하고자 했는데, 반도체 대란으로 장비 수급에 문제가 있었다. 그래서 우선 사내에서 제공하는 Redis HA(High Availability)로 전환하기로 했다. 도메인 기반 스위칭을 이용한 고가용성을 제공하는 사내 솔루션으로, 일반 Redis처럼 캐시 및 스토리지 사용이 가능하지만, Sharding을 미지원하는 문제가 있었다.

Sharding은 데이터를 분산 저장하는 기법으로, 데이터를 분산하는 주체에 따라 클라이언트 사이드와 서버 사이드로 나눌 수 있다. 클라이언트와 서버는 데이터 스토어 관점에서 본 것으로, 클라이언트가 애플리케이션, 서버가 데이터 스토어가 된다.

 

계정 캐시는 50만 TPS를 처리하는 캐시 클러스터였기 때문에 단일 노드로 서비스할 수 없었다. 즉, 서버 사이드 Sharding을 할 수 없었기 때문에 클라이언트 사이드 Sharding을 시도하게 된다.

 

클라이언트 Sharding은 애플리케이션 서버에서 여러 캐시 서버로 데이터를 분배하는 방법으로, 일반적으로 키를 기준으로 노드를 선정한다. Archus처럼 캐시 노드를 관리하기 위한 Zookeeper를 추가할까 했지만, Redis 클러스터로 또다시 변경할 것이기 때문에 추가하지는 않았다.

 

Jedis는 Java 기반의 Redis 클라이언트 라이브러리로, Thread-safety 하지 않다는 특징이 있다. 근데 Jedis에서 클라이언트 사이드 Sharding을 지원(Shareded Jedis)하고 있다. 
Shareded Jedis는 입력된 키를 해시하고 해시값으로 해시링에 위치한 노드를 찾는 알고리즘이다. 노드 추가 또는 삭제 시 Rebalancing 데이터가 최소화된다.

 

이때 이슈가 하나 있었는데, 멀티키 문제였다. Shareded Jedis에서는 멀티키에 대한 지원을 하지 않고 있었다. 이를 해결하기 위해 클라이언트 Sharding이 키를 기준으로 노드를 선정한다는 것을 이용했다. 멀티키 명령어를 수행할 수 있도록 자체 개발을 한 것이다. 

기본 아이디어는 키를 기준으로 대상 Redis 호스트 정보를 추출해 호스트별로 키를 그룹핑하고, 그룹핑된 키 리스트를 단일 노드에 요청하는 것처럼 연산을 수행하는 것이다.

 

이번에는 캐시 트래픽 전환 과정이 필요하다.

캐시 트래픽 전환 요구사항

계정을 이용한 서비스는 굉장히 많다. 캐시 작업이 트래픽이 적은 새벽 시간대에 완전히 끝날 수도 보장할 수 없다. 그래서 무중단 전환으로 진행했다.

단순히 Archus로 가는 트래픽을 Redis로 전환한다면, 캐시 된 정보가 없기 때문에 모조리 DB로 트래픽이 넘어갈 것이다. 트래픽이 많으면 DB가 죽을 수도 있다. 따라서, Redis로 전환하기 전에 미리 Redis로 Archus의 데이터를 복제했다. 캐시 데이터 조회는 기존처럼 Archus에서만 수행하되, 캐시 데이터 세팅 시에 Archus와 Redis 양쪽으로 모두 저장하는 방식으로 진행했다.

Redis로 데이터 이전 후 아무런 이슈가 없다면 트래픽 전환 작업을 수행하면 된다. 하지만, 이슈가 생겨 롤백이 필요하다면 다르다. Redis로 트래픽이 전환되는 동안 Archus와 Redis의 캐시 데이터 불일치가 발생할 수 있다.  따라서 단순히 롤백은 문제가 될 수 있다. 이때는 반대로 Redis의 데이터를 Archus로 복제 후 롤백을 하도록 했다.

 

Archus를 제거하는 시점은 모니터링을 통해 결정해야 한다. 약 2주간의 모니터링 후 Archus를 제거했다.

 

Canary Release를 이용해 Redis 코드를 배포했다. 계정 캐시 서버는 50만 트래픽을 처리하고 있기 때문에 전환 작업에 문제가 생긴다면 최대 초당 50만 건의 에러를 보게 된다. 따라서, 특정 서버들에만 Redis 코드를 배포해 가면서 점진적으로 서버들을 Archus로부터 Redis로 전환을 시도했다.

 

사실 생각해 보면 카카오 같은 기업에서 캐시 서버 전환 시 무중단 전환 / 점진적 전환하는 것들은 당연한 것 같다. 내용 자체는 듣기에는 되게 심심한 내용인 것 같다. 실제 현업에서 일하면서 얻은 경험은 차원이 다르겠지만 말이다. Archus와 Redis에 대한 조사도 엄청 했을 것이고, 적절한 전환인지 논의도 엄청 많았을 것이다. 두 캐시 서버 사이의 동기화 과정도 고민을 했었을 텐데, 다 된 결과물만 들었을 때는 심심한 것 같다.


알림 서비스로 시작하는 서버 개발

카카오뱅크의 서버개발자 분이 나와서 카카오뱅크의 알림 서비스에 대해 설명해 주신다.

 

기본적인 알림 서버 구조

기본적인 알림 서버 구조에 대해 설명한다. 알림 생성 이벤트가 발생하는 서버는 알림 서버에 알림 생성 요청을 한다. 알림 서버는 DB에 알림을 생성해 놓게 되고, 사용자가 앱으로 알림 메시지를 조회하게 되는 구조다. 이렇게, 알림 생성과 알림 조회 두 가지 책임을 갖는 알림 서버가 만들어진다.

 

단, 트래픽이 적은 서비스라면 이와 같은 구조로 충분히 운영 가능하다. 하지만, 갑작스러운 이체나 카드 사용률이 많아져 알림 생성 요청이 증가하게 된다면 알림 서버에 부하가 생길 것이다. 알림 서버 CPU 사용률이 100%가 되거나 시스템 스레드가 고갈될 것이다. 이 경우에는 알림 생성을 요청하는 서버 쪽과 알림 조회를 하는 사용자에게까지 불편이 전파된다.

 

알림 생성 서버와 알림 조회 서버의 분리

알림 생성과 알림  조회의 장애를 격리시키기 위해 각각의 책임을 갖는 생성 서버와 조회 서버를 분리했다. 생성 서버에 장애가 발생하더라도 조회 서버는 원활하게 서비스를 제공할 수 있다. 관리 포인트(서버)가 증가하기는 하지만, 서비스를 제공할 수 있다는 이점이 생기게 된다.

 

조회 서버에 대한 장애 전파는 차단할 수 있지만, 추가적으로 생성 요청을 하는 연계 시스템에 대한 장애 전파도 막을 수 있어야 한다.

생성 서버의 장애로 인해 응답이 지연된다면, 생성을 요청했던 연계 시스템으로 기본적인 자원이 고갈되게 된다. 부수적인 알림 생성에서 문제가 생기자 중요한 이체 기능을 수행하지 못하는 식의 문제가 발생할 수 있다. 즉, 간접적으로 다시 사용자에게 장애가 전파되는 것이다.

 

연계 시스템이 할 수 있는 방법으로는 몇 가지가 있다.

  • 타임아웃 설정
  • 비동기 알림 생성 요청

 

비동기 알림 생성 처리

이는 연계 서버가 알림 생성 서버를 신뢰하지 못하게 된 것이다. 신뢰 있는 생성 서버가 되려면 생성 서버 내부에서 비동기로 알림을 생성하도록 하면 된다. 생성 서버는 알림 생성 요청 수신 시 알림 생성 작업을 비동기로 수행하고 빠르게 응답을 주도록 한다.

알림 생성 서버 내에서 비동기로 처리하게 됨으로써 알림 요청과 알림 생성에 경계가 생기게 된다. 덕분에 메인 비즈니스인 알림 생성 로직이 무거워지거나 알림 생성 요청을 한 연계 시스템은 큰 영향을 받지 않게 된다. 즉, 알림 생성의 장애가 연계 시스템으로 전파되지 않는 것이다.

이로써 비동기를 사용해 빠른 응답 시간과 장애 격리에 대한 이점을 가졌다.

 

이제 연계 시스템이 알림 메시지가 정상적으로 만들어졌는지는 알 수 없다. 알림 생성 요청에 대한 응답만 먼저 수신하기 때문이다. 이를 보완하기 위해 알림 생성 실패 내역을 조회할 수 있는 API를 만들었다. 또한, 실시간으로 알림을 만들어주지는 못해 약간의 지연이 존재할 수 있어 바로 조회가 안 될 수 있다. 이는 지연에 대한 모니터링을 지속적으로 수행하고 있다.

 

비동기로 알림 생성 중에 생성 서버가 다운되거나 재시작하면 알림은 유실되어 없어지게 된다. 운영 신뢰성을 높이려는 비동기 방식이 역으로 데이터 신뢰성을 보장할 수 없게 된 것이다. 지속적으로 알림이 유실되는지 확인하거나 유실된 알림은 재처리하는 로직이 추가로 필요하겠지만, 알림 유실에 대비한 개발이나 관리 포인트가 증가하게 된다.

 

메시지 큐 도입

문제가 된 알림 유실 관리 포인트는 메시지 큐를 도입해 줄여보도록 했다. 생성 서버는 생성 요청이 올 시 메시지 큐에 enqueue를 한 뒤 연계 시스템(프로듀서)에 빠르게 응답한다. 이후 생성 처리 로직(컨슈머)으로 입력된 메시지를 dequeue 해, 알림 생성 비즈니스 로직을 거친 뒤 DB에 알림을 저장하게 된다.

 

메시지 큐 또한 비동기성을 띄고 있어 비동기 방식 특징이 그대로 유지되고 있다. 게다가 알림 생성의 장애나 지연 알림 요청으로 장애 전파 확률이 낮아졌다.

 

생성 서버가 다운되거나 재시작되면 알림이 유실되던 문제도 해결됐다. 메시지 큐의 개념을 알 필요가 있는데, 컨슈머가 알림 요청 메시지를 dequeue 한 뒤 처리 로직을 수행해 DB에 저장을 마치면 메시지 큐에 ACK를 보내게 된다. 그제야 메시지 큐는 작업 완료 처리를 하게 된다. 즉, 컨슈머가 장애가 발생하게 되면 ACK를 메시지 큐에 보내지 못한 것이고, 이는 후에 메시지 재처리가 가능하다는 것이다.

 

메시지 큐 장애시 처리 방안

이제 메시지 큐에 장애가 발생할 수 있는 건에 대해 고민해야 한다. 고가용성을 위해 잘 만들어진 시스템이지만, 안정적 서비스를 위해 고려해 볼 필요가 있다. 기본적으로 메시지 큐는 갖고 있는 이벤트를 디스크에도 저장하고 있어, 메시지 큐가 장애로 재시작하더라도 디스크에 있는 데이터로 작업을 처리할 수 있다.

 

그런데, 메시지 큐에 장애가 발생해 요청이 들어와도 enqueue 작업을 하지 못하는 상황이 있을 수 있다. 따라서 생성 서버는 메시지 요청을 메시지 큐에 넣기 전에 별도 DB에 저장하게 된다. 메시지 큐가 다운되더라도 미쳐 enqueue 하지 못한 메시지들을 재처리할 수 있게 된다.

 

알림 조회와 알림 생성

이제 알림 조회 구조를 볼 차례다. 알림 생성과 달리 간단한 구조로 되어 있다. 알림 서비스는 안정적이고 빠른 서비스를 제공할 수 있도록 처리한 구조다. 조회는 간단히 구현되었고 굵직한 비즈니스 로직은 알림 생성에서 담당하게 되었다.

 

 

이후에는 추가적인 개발 요청 건들에 대해 처리한 방식을 설명한다. 오래된 알림 서비스 조회 요청, 오래된 알림 서비스 주기적 삭제 요청, 증가하는 트래픽에 대한 고민이 그것이다.

 

오래된 알림 조회 수행을 위한 내부 DB 분리

먼저, 오래된 알림 조회 요청을 살펴보자. 기본적으로 90일간 제공하는 알림 서비스이지만, 고객센터에서 응대를 위해서는 12개월 전까지의 알림 조회가 필요했다. 또한, 운영 팀에서는 많은 양의 데이터를 다운로드/업로드하는 무거운 작업에 대한 요청이 있었다.

장기간 데이터에 대한 쿼리와 같이 비용이 큰 작업은 서비스에 영향을 주지 않기 위해 별도 내부 서비스를 위한 서버를 구성했다.

 

최장 12개월 알림 조회가 있어야 하지만, 주로 최근 3개월의 데이터만 조회되기 때문에 각각의 요구사항에 맞는 DB를 서비스 DB와 아카이브 DB로 분리했다. 서비스 DB는 CRUD가 빈번하거나 빠른 성능이 필요하지만, 아카이브 DB는 데이터 변경이 없고 낮은 빈도 외 조회만 요해 성능이 요구되지는 않는다. 이로 인해 아카이브 DB는 데이터 압축률이 좋은 엔진을 사용했다.

 

오래된 알림 파기 수행을 위한 파티셔닝 도입

이번에는 오래된 데이터에 대한 파기에 대해 고민을 해봤다. 서비스 DB 기준으로는 3개월의 데이터만 필요하기 때문에 3개월이 지나면 파기해야 한다. 이때 데이터 삭제를 DELETE 명령어를 주기적으로 사용한다면, 락이 걸린다거나 많은 부하가 발생할 수 있어, 서비스에도 직접적으로 영향을 줄 수 있다.

 

이를 해결하기 위해 하나의 테이블에 4개월치 로그를 담는 것이 아니라, DB가 제공해 주는 파티셔닝을 이용했다. 파티셔닝은 논리적으로 동일한 테이블이지만 물리적으로는 다른 테이블에 저장하는 기법이다. 즉, 개별 데이터가 아니라 하나의 테이블을 삭제하는 방식으로 작업을 하게 되는 것이다. 이 경우 무리가 되는 부수적 작업이 없어 안정적으로 서비스 제공이 가능하다.

 

단, 파티셔닝에서 어떤 파티션으로 가야 하는지에 대한 조건을 지정해줘야 한다. 12월의 데이터를 찾기 위해 조건을 주어야 하는데, 그렇지 않다면 모든 파티션을 스캔할 수도 있게 된다. 이는 성능의 악영향을 준다. 즉, 유지보수 측면에서 이점은 있지만 애플리케이션이 복잡해지는 단점이 있다.

 

늘어나는 알림 트래픽

마지막으로 점점 커져가는 서비스에 대한 준비다. 이는 두 가지 방식을 사용했다. 먼저 애플리케이션 스케일 아웃을 통해 요청을 처리하는 애플리케이션을 병렬적으로 늘려 기존 대비 늘어난 트래픽을 소화할 수 있도록 했다. 두 번째로 늘어나는 애플리케이션에 맞춰 DB 증설도 수행했다. DB는 Sharding을 통해 데이터를 분배할 수 있었다. 하지만, DB Sharding은 애플리케이션이 복잡해지는 단점이 있긴 하다. 라우팅 조건이 붙게 되고, DB가 증설될 때는 변경된 라우팅 조건에 맞게 데이터 이관 작업이 필요하기 때문이다.

 

이 세션이 이렇게까지 영양도가 높을 줄 몰랐다. 발표자료도 깔끔하고 발표도 깔끔하고 내용도 나에게는 흥미로웠다. 비동기 처리와 메시지 큐에 대해 요즘 관심이 생겼었기 때문에 어떤 장단점이 있어 어떤 방식으로 결정했다는 점들이 흥미로웠다. 각 부분마다 우리 회사에 적용해서 문제를 바라보면서 세션을 들었다.


ㄷㄷㄷ : Domain Driven Design과 적용 사례 공유

Monilithic 한 Legacy Server를 MSA 적용을 위해 DDD 적용을 한 사례에 대해 공유한다.

DDD는 각 기능적 문제 영역들을 정의하는 도메인과 도메인을 사용하는 비즈니스 로직 중심의 설계를 말한다. 데이터 중심이 아닌 도메인의 모델과 로직에 집중한다는 특징이 있다. 또한, 동일한 표현과 단어로 구성된 단일화된 언어 체계(Ubiquitous Language)를 사용한다는 특징이 있다. 기업과 개발, 사업까지 팀 내부적으로 단일화된 커뮤니케이션 방식을 이야기하는 것이다. 마지막으로, 소프트웨어 엔티티와 도메인 간의 개념이 일치하다는 특징이 있다. 분석 모델과 설계, 코드가 각각 다른 구조가 아니라, 도메인 모델부터 코드까지 항상 함께 움직이는 구조의 모델을 지향한다.

 

TDD vs BDD vs DDD 접근방

TDD / BDD가 아닌 DDD를 사용한 이유는 다음과 같다. DDD를 선택한 이유는 개선하기로 한 파트너 사이트는 레거시 서버로써 이미 큰 틀과 검증된 플로우가 존재하고 있었기 때문이다. 이를 전체적으로 갈아엎는 빅뱅 방식 대신 조금씩 필요한 기능을 구별하고 필터링해 개선하는 방향을 선택했기 때문이다.

 

프로젝트를 진행하다 보면 항상 인력 리소스와 시간이 부족한 부분이 있다. 이런 리소스 측면에서 더 유용하게 가동하고자 레거시 서버와 신규 서버를 동시에 가동할 필요가 있었다. 그래서 DDD를 선택하게 되었다.

 

DDD에서 중요한 세 가

DDD를 적용하기 위해 선행되어야 하는 작업이 있었다.

  • Bounded Context : 범위를 구분해 놓은 하위 도메인 개념
  • Context Map : Bounded Context 간의 관계 표시
  • Aggregate : 데이터 변경 단위(라이프 사이클이 같은 도메인을 모아놓은 집합)

파트너사의 기능 분류

파트너 사이트에서 제공하는 기능 영역을 식별하고, 각 영역들을 분류해 의존성을 낮추기 위해 경계를 두었다. Bounded Context를 구분하게 되면 MSA처럼 각각의 서비스로 나뉘게 된다. 서로 데이터 조회 등을 위해서는 API를 사용하고, 각 도메인을 철저히 분리하게 만드는 것이다.

 

Bounded Map 바탕의 Context Map

Context Map을 구성해 Upstream과 Downstream을 한눈에 볼 수 있게 해, 전체적인 흐름을 예상할 수 있었다.

 

Aggregate 예

Context Map을 바탕으로 서비스를 구현할 때 서비스들이 갖는 많은 객체들을 그대로 사용하는 것은 의미가 없어, 각 도메인 객체들의 집합을 설계하게 됐다. 이것이 Aggregate다.

 

Aggregate의 특징은 데이터 변경 단위라는 것과, 접근은 루트 엔티티를 통해서만 가능하다는 것이다. 라이프 사이클이 비슷한 도메인을 모은 집합인 만큼 모든 CRUD는 루트 엔티티를 통해 내부의 모든 데이터에 영향을 미치게 된다.

예를 들어, 비디오 프로덕트 내부의 인코딩 정보와 인코딩 파일에 대한 정보는 개별적으로 접근이 불가능하다. 오로지 비디오 프로덕트를 통해서만 접근하고 수정, 삭제할 수 있다.

Aggregate를 사용하면 개별 객체 간 상호작용보다는 객체들 사이의 관계를 조금 더 넓은 시야로 바라볼 수 있다. 조금 더 비즈니스 차원에서 바라볼 수 있게 된다.

 

DDD 대표 아키텍처 세가지
Layered Architecture 대표적 구성 (4계층)
Celan Architecture 대표적 구성 (4단계)
Hexagonal Architecture 대표적 구성

팀에서는 세 가지 설계 방식을 비교했다. Layered 아키텍처는 비즈니스 로직이 거대해지면서 애플리케이션 레이어가 오염되는 경우가 연결되어 제외했다. Celan 아키텍처보다는 Port and Adapter라는 명확한 구현 개념이 있는 Hexagonal 아키텍처를 선택했다.

 

적용된 형태

이렇게 적용한 후에는 장점만 있던 것은 아니었다. 아래와 같은 단점이 있었다.

  1. MSA의 단점 (복잡해진 개발 난이도, 높은 숙련도, 나눠진 서버의 트랜잭션 관리, 통합 테스트 어려움, 배포 복잡도)
  2. Hexagonal 아키텍처 구현에서 도메인을 위해 추가되는 많은 코드
  3. 도메인에 대한 높은 이해도 요구

 

장점은 아래와 같다.

  1. 보편적 언어 사용으로 인해 빠른 커뮤니케이션 가능 (데이터 관점의 명칭들, 웹에서 사용했던 용어들, 영업에서 사용했던 언어들 통합)
  2. 도메인 관계가 복잡한 경우 Aggregate로 큰 틀로 정리 가능
  3. 복잡한 도메인의 분리로 유지보수에 대한 편의성
  4. 새로운 기능이나 요구사항에 대한 유연성
  5. Aggregate 사용으로 도메인의 캡슐화
  6. 유스케이스나 포트 사용 등으로 의존도를 낮췄고, 도메인 로직과 서비스 로직을 필요한 곳에 모음으로써 응집도를 높임
  7. 도메인 로직의 분리로, 개발자는 비즈니스 로직에 집중 가능
  8. 코드에 대한 가독성 향상

항상 레거시 시스템의 전환은 고민스럽다. 그렇기 때문에 오히려 고통스러운 레거시를 청산하고자 공을 들여 도메인에 대해 이해하는 것이 필요하다.

 

최근 들어 MSA를 적용하고 싶다는 욕망이 컸는데 이 세션을 들으니 오히려 줄어들었다. 전환하기에 너무 많은 것들을 고려해야 한다. 마지막에서 발표자분이 말씀하셨던 것처럼 고민스럽다. 패키지를 파는 회사에서 전환이 필요할까? 점진적 변경이 어려운 상황이지 않을까? 유지보수나 유연성은 확보되더라도 더 높은 난이도의 개발과 도메인에 대한 높은 이해도는 입사자가 왔을 때 더 어렵지는 않을까? 도메인 중심으로 구성되면 더 이해하기 쉬워지는 건가?

그동안 내가 너무 큰 생각 없이 MSA를 해보고 싶었던 것 같다. 회사 입장에서 생각해 보면 쉽지만은 않는 게 확실해 보인다. 스스로도 좀 더 고민이 필요해 보인다.


Technical Writing: 글로 하는 의사소통

이 세션만 세 번째 보면서 글을 쓰고 있다. 첫 번째는 컴퓨터 프리징 때문에 멈춰서 날려버렸고.. 두 번째는 노트패드에 쓰던 습관대로 블로그로 바로 글을 쓰다가 저장하지 않고 컴퓨터를 껐다..

 

세션을 시작하면서 바로 좋은 말을 하나 해주고 시작한다. '글을 쓰는 것은 쉽지만 글을 더 좋게 쓰는 것은 어렵다.'

 

테크니컬 라이팅의 중요성

테크니컬 라이팅에 대한 대원칙을 설명하기 전에 목표를 먼저 설정하겠다. 우리의 최종 목표는 정보를 독자에게 효과적으로 전달하는 것이다. 목표를 위한 수단으로 테크니컬 라이팅을 사용할 것이다.

대원칙 두 가지를 정의하겠다. 우리의 목표를 위해서는 내가 말하고 싶은 내용을 쓰는 것이 아니라 상대가 알아야 할 내용을 쉽게 알려줘야 한다는 것이다. 그리고, 독자 N명의 시간 대신 나 하나의 시간을 더 할애해야 한다.

 

아래의 상세 원칙들은 위키백과와 구글 테크니컬 라이팅 코스 등에서 발췌해 정리한 내용을 설명한다.

 

글쓰기에는 세 단계가 있다.

  1. 글 쓰기 전
  2. 글 쓰는 중
  3. 글 쓴 뒤

테크니컬 라이팅 상세 원칙

글쓰기 단계별로 상세 원칙들이 많아 보이지만 많은 원칙들이 실제로는 익숙하게 은연중에라도 사용해 오던 것들이다. 단계별로 설명하겠다.

 

먼저 글 쓰기 전 단계에서 해야 할 것들이다. 글을 쓰기 전에는 간단하게라도 어떤 내용을 쓸지 계획을 정리한다. 계획된 글쓰기는 잘 짜인 글이 되고, 이해를 쉽게 하며 읽는 시간을 줄이게 만들어 줄 것이다.

다음으로 독자를 파악해야 한다. 전달하고자 하는 내용이 아무리 좋더라도 독자에 맞는 내용이 전달되지 않으면 독자는 그 정보를 100% 수용하기 어렵다. 글의 설명 수준을 결정하는 중요한 요소가 된다. 독자를 몇 가지 분류로 구분해 보고 어떤 정보를 습득해야 할지 정리해봐야 한다. 이는 목차 구성에 도움이 된다.

목차를 구성해야 하는데, 주제와 독자에 맞게 어떤 구조로 내용을 전달할지를 고민하는 단계다. 처음에는 정리한 목차에 간략하게 어떤 내용을 작성할지 코멘트를 남기는 것도 추후에 내용 작성에 도움이 된다.

 

이제 뼈대 위에 글을 쓰는 작업을 해야 한다. 본격적인 테크니컬 라이팅에 해당하게 되며, 다양한 원칙 중 8가지만 설명한다. 먼저 두괄식 구성은 결론을 스포 하는 것이다. 핵심을 독자에게 먼저 인지시키고 이후에 보충 내용을 서술해야 하며 문서 어디에도 스펙터클한 반전이 없어야 한다.

일관성 유지는 독자가 글을 읽으면서 헷갈리지 않도록 하는 목적이다. 하나의 단어를 글 전체에서 여러 표현으로 작성할 경우 독자는 이해하기 어려워진다. 긴 시간에 글을 나눠서 쓰는 경우에 자주 발생한다. 정보 전달글에서는 독자가 처음 접하는 용어가 많을 수밖에 없는데, 그 와중에 하나의 단어를 여러 표현으로 복잡하게 만들면 안 된다.

하나의 문장이 길어지면, 호흡이 길다고 표현한다. 긴 문장을 독자는 머릿속에서 다시 분해하고 이해하는 과정이 필요하다. 따라서 짧게 글을 쓰도록 하자. 이때 미사여구나 형용사 사용을 줄이는 것도 방법이다.

정보 전달 글에서는 하나의 긴 문장보다 개조식 표현이 더 깔끔하다. 개조식 표현이 아닌 경우, 문장이 많아서 복잡하게 보일 수 있다.

한국어는 영어와 다르게 수동태 표현이 번역체처럼 부자연스럽다. 따라서 능동형 문장으로 표현을 바꿔주도록 하자.

인간의 뇌는 부정형을 이해하지 못한다. 부정문은 강조하는 효과가 크다. 따라서 긍정문으로 바꿔 표현하는 것이 전달력이 좋다. 대부분의 경우 이중 부정으로 강한 긍정을 표현할 일도 없다.

대명사(그것, 이것, 이전 등) 사용을 줄이는 것이 좋다. 일상 대화에서는 대화의 주제가 명확하고 눈높이가 맞춰져 있기 때문에 대명사를 써도 거리낌이 없지만, 독자가 글을 읽을 때는 이전 문장을 계속 찾아가며 글을 읽을 수도 있다.

글을 잘 작성하면 시각적 요소는 생략이 가능하지만, 대부분의 기술 문서나 정보 전달 글에서는 시각적 요소를 사용하는 것이 좋다. 순서 정보, 수치, 데이터 등을 보여줘야 할 때가 많기 때문이다.

 

마지막으로 글을 쓴 뒤 검토 단계다. 내 글에 익숙해지게 되면 잘못된 부분을 발견하기 어려워진다. 쓰기 전 단계와 쓰는 중 단계에서의 테크니컬 라이팅의 원칙이 잘 적용되었는지를 확인하는 것이 주된 내용이다. 가장 먼저 내용 흐름을 확인한다. 서론에서는 글 작성의 목적과 주제를 설명하는지, 본론에서는 각 문단의 내용과 제목(대제목, 소제목)이 일치하는지를 확인해야 한다. 결론에서는 글의 작성 목적이 간략하게 포함되어 있어야 하며, 어떤 내용을 설명했는지가 결론만 보고도 이해할 수 있어야 한다.
용어나 표현이 일관성이 유지되었는지 확인해야 한다. 오타뿐만 아니라 문단의 흐름에 맞지 않는 문장 또한 잘못된 것이다. 설명이 부족하면 이해가 덜 되는 것이지만, 흐름이 이상하면 이해를 어렵게 만드는 것이다. 이때 삭제해도 내용 이해에 부족함이 없다면 과감하게 지워주는 것이 좋다.

용어나 약어는 충분히 설명이 필요하다. 필요에 따라 약어집을 별도로 정리해 주는 것도 도움이 된다.

마지막으로 구두로 읽어보는 검토법이 필요하다. 눈으로 읽을 때는 자연스러워도 구두로 읽을 때 어색할 수 있다. 호흡이 긴 문장도 찾을 수 있다.

 

마지막으로 5가지 팁이 있다.

  1. 어제의 코드가 Legacy가 되듯, 어제의 문서가 Legacy가 된다. 내용에 변화가 있다면 문서도 같이 갱신해 주도록 하자.
  2. 한 문서에 모든 것을 담을 필요가 없다. 문서도 마이크로 서비스화가 필요하다. 도메인별 계층별로 분리해 작성해 주자.
  3. 레퍼런스 주소는 대체 텍스트를 같이 넣어주자. 링크가 깨졌을 경우를 대비해 검색 키워드를 제공해 주는 역할을 할 수 있다.
  4. 올바른 조사를 사용해야 글이 자연스럽게 읽힌다. 영어는 읽을 때 자연스러운 조사를 사용하면 된다.
  5. 영어는 괄호 앞에 띄어쓰기가 들어가고, 한국어는 괄호 앞에 띄어쓰기가 없다.

이력서 작성이 처음이라면, 채용팀이 알려주는 개발자 이력서 작성 꿀팁!

카카오페이 인재영입팀 소속이신 분이 나와서 진행해 주신다. 5년간 카카오페이 채용담당자로 있으셨다니 내 취준 시기에 있던 분이다. 그래서 그 당시 카카오를 생각해 보면 어떤 것들을 중요시해왔을까 궁금했다.

 

최근 채용 트렌드부터 살펴보자. 채용 과정(특히 지원서 작성)의 간소화가 가장 큰 트렌드로 볼 수 있다. 과거에는 최대한 많은 스펙을 나열하듯이 보여주고 기업마다 다양한 자기소개서 항목을 작성해야 하는 경우가 많았다. 요즘은 서류 접수를 파일 하나도 대체하거나 포트폴리오 링크로 표현하는 등 간소화된 방식으로 진행된다. 따라서, 본인을 잘 어필할 수 있는 잘 만들어진 이력사 하나는 꼭 갖고 있도록 하자.

 

잘 만든 이력서란 프로덕트를 만들 때 유저 입장에서 UI/UX를 고민하던 것처럼 읽는 사람을 생각한 이력서다. 즉, 가독성이 중요하다. 대부분 IT 기업에서는 현업 개발자가 서류전형 단계에서부터 채용에 참여를 하기 때문에 현업 개발자 입장으로 잘 이해될만한 이력서 작성이 중요하다.

 

이력서로 커뮤니케이션하기 세 가지

매력적인 이력서를 만들도록 해보자. 이력서로 잘 커뮤니케이션하는 방식은 세 가지로 꼽아 볼 수 있다. 첫 번째로 무엇을 했는지, 두 번째는 어떻게 했는지, 세 번째는 꾸준히 상정 가능할지에 대해서다.

 

무엇을 했는지

본인이 진행한 프로젝트만 작성하는 것이 아닌, 어떤 내용과 결과로 이루어졌는지 작성해야 한다. 이력서를 검토하는 사람은 진행한 업무의 이름만 보고는 파악할 수 없다.

 

어떻게 했는지

실제 개발역량은 무엇을 했는지만 보고는 파악하기 어렵다. 상세한 HOW가 담겨있어야 매력적인 이력서가 될 수 있다.

 

성장할 수 있는지

개발자는 꾸준히 성장이 필요한 직업이다. 이런 점은 개선과 관련해서 작성하는 것이 쉬우며, 수치로 작성해 주는 것이 파악하기 쉽다. 많은 개발자들이 업무 외에도 별도로 스터디도 진행하고 토이 프로젝트도 진행하며 별도 학습을 하는데, 이 부분은 성장 가능성에 대한 부분으로 분명히 도움이 되는 부분이다. 학습의 주기나 내용, 결과물과 같은 분명한 히스토리를 보여줄 수 있다면 더 좋다.

 

실제 이력서 작성 시 고민해야 하는 내용들이 대해 실감되는 꿀팁들을 설명한다.

 

첫 번째로 파일 양식이다. 카카오페이는 자유 양식이라 워드, 한글, PDF, 노션 링크, 알 수 없는 압축된 파일 형태 등 다양한 양식으로 이력서가 온다. 그렇지만 이력서는 PDF로 제출하는 게 가장 좋다. 현업에서 검토하는 경우는 모두가 워드나 한글을 열람할 수 없을 수도 있다. (카카오는 모두 Mac을 쓰니..)

두 번째로 분량이다. 영문 resume는 한 장 작성이 원칙이지만 한글로 작성 시에는 3 장 이내로 작성하는 것이 가장 좋다. 내가 한 모든 활동을 다 보여주기 위해 10 장의 이력서를 적는 것보다는 강점이 있는 역량을 간결히 표현하고 포트 폴리오와 같은 것들로 부수적으로 표현해 주는 것이 더 좋다.

세 번째는 직무 유관 경험이다. 이력서에서 가장 중점적으로 확인하는 것이 직무 관련 경험이다. 그다음이 경력이나 기술 스택 일치 여부다. 이런 경력은 꼭 회사 경력만 말하는 것은 아니다. 학부 시장의 프로젝트 경험이나 스터디 경험 모두 유관 경험이다.

유관 경험이 없을 때 어떻게 해야 하는지는 지원하는 포지션에 따라 다를 것 같다. 경험이 많지 않은 주니어일수록 본인이 경험한 모든 기술과 프로젝트 공부 내용을 이력서에 다 담으려고 할 것이다. 그건 한 포지션의 스페셜한 이력서라기보다는 제너럴 한 이력서가 된다. 지원하고자 하는 포지션의 기술 스택과 맞지 않는 경험은 적지 않는 것이 좋다고 한다.

네 번째는 개인 흥미와 로열티를 나타내는 방식이다. 하나의 일반적 양식으로 지원서를 만들고 사용해도 좋지만, 기업에 실제 지원 시에는 그 기업의 서비스를 직접 개발하는 분들에게서 흥미를 가지도록 해야 한다. JD (Job Description)의 기술 스택 및 채용 요건, 공식 사이트(회사 채용 사이트, 기술 블로그 등)의 내용, 서비스 자체에 대한 관심도 중요하지만 기술적 부분에서의 탐구 내용이 포함되는 것이 흥미를 이끌어내기 좋은 소스다.

마지막으로, 포트폴리오에 대한 내용이다. 이력서 외의 본인 성과나 역량을 보여줄 수 있는 추가 자료를 의미한다. 디자이너는 디자인 작업물, 기획자는 기획한 스토리보드와 기획서, 개발자는 본인의 코드나 학습 기록이 그것이다. 노션이나 깃허브와 같이 개인 DB를 꾸준히 관리하면 자기 계발을 하고 있다는 증거가 된다. 이력서가 부족하더라도 매일 연구하고 공부했다는 이력이 있으면 성장 가능 인재라 판단할 수 있다. 단, 개인 DB를 포트폴리오로 사용하는 건 꾸준한 학습에 대한 증명이기 때문에 성실함도 중요하다. 너무 옛날 글이거나 글이 별로 없다면 역효과가 날 수도 있다.

 

다양한 내용이 더 추가 가능하다.

 

작성할 내용이 많더라도 모든 내용을 작성하게 되면 읽는 사람인 평가자는 과도한 정보로 피로도를 느끼고 중요한 정보를 놓칠 수도 있다.

 

예를 들어 독서 목록으로 기술 관련으로 제출하는 경우가 있다. 현업에서는 독서 목록보다는 어떤 책을 읽음으로써 어떤 기술을 실제 개발 업무에 적용했고 어떻게 활용했는지가 더 중요하다.


728x90