채용공고 올리기

김도현님을 응원해보세요!

지금 만족하고 있어요

미리보기

기본 정보

이름
김도현
직업
백엔드 개발자
간단 소개

자기소개

자기소개

좋은 사용자 경험을 제공하기 위해서 기능적 요구사항을 충족하는 데 그치지 않고

신뢰성, 가용성, 유지보수성과 같은 비기능적 요구사항에도 집중하여 디테일한 차별점을 가진 개발자가 되고자합니다.

신뢰성을 확보하기 위해 인수 테스트 코드를 추상화하여 사용 사례에 대한 테스트를 신속하게 대응할 수 있는

환경을 구축한 경험이 있습니다.

가용성을 확보하기 위해 캐싱과 서버 간 통신 딜레이 단축 등 성능 개선에 집중하여 학습하였으며, Caffeine Cache 딥 다이브와 gRPC 통신 적용에 관한 경험이 있습니다.

유지보수성을 확보하기 위해 기능의 추가/변경에 빠르게 대응할 수 있도록 고민하며 코드를 작성하고자 합니다.

개인 프로젝트에서 관련 방법론을 적용하며 업무환경에서 필요시 의견을 낼 수 있도록 지속적인 관심을 갖고 있습니다.

새롭게 접한 기술에 관한 학습, 직접 겪은 트러블 슈팅 과정을 기록한 약 100개의 게시글을 보유한 블로그를 운영하고 있습니다.

여러 사람의 의견을 듣고 토론하는 활동을 좋아하여 IT 개발 동아리 Mash-Up에서 스터디, 프로젝트, 세미나 등을 진행하고 있습니다.

기술 스택

기술 스택

Java, Kotlin, Spring Boot, Spring Security, JPA, Kafka, Redis, MySQL, MSSQL

경력

회사명

(주)스마일게이트홀딩스

직급 | 부서 | 근무 유형

주임 | DWP개발팀 | 재직 중

근무 기간

2024.04. ~ 재직 중 (9개월)

담당 업무

KRaft Mode의 Kafka Cluster 구축을 통한 Zookeeper 의존성 제거 및 이벤트 스트리밍 플랫폼 고도화

  • 문제

    • 마이크로서비스 아키텍처에서 서비스 간 결합도를 낮추기 위한 메시징 플랫폼 필요

    • Zookeeper 기반 Kafka 구성 시 추가적인 의존성 및 운영 부담 발생 우려

    • 내부 근태관리 시스템의 데이터 무결성 보장 요구사항 존재

  • 해결과정

    • 팀 내 KRaft Mode에 대한 R&D를 수행하고 기술 전파

    • Zookeeper 의존성을 제거한 KRaft Mode 기반의 Kafka Cluster 구축

    • Kafka 표준 코드를 도입하여 데이터 유실 가능성을 최소화한 아키텍처 설계

    • 프로듀서의 메시지 전송 보장 옵션과 컨슈머의 트랜잭션 코디네이터를 활용한 'Exactly Once' 전송 구현

Kafka Connect를 활용한 CDC Pipeline 구축으로 Legacy DB Event Migration 환경 구현

  • 문제

    • 레거시 DB에서 발생하는 DB 이벤트를 신규 DB로 이관 및 실시간 데이터 변경사항 동기화 요구사항 발생

    • 레거시 DB에는 약 4천만 건의 데이터가 보관되어있으며 현재도 실시간으로 데이터 쓰기 작업이 발생하는 상황

  • 해결과정

    • 팀 내부에서는 SP방식으로 해결하고자 하였으나 성능적인 문제가 발생할 것을 우려하여 데이터 파이프라인에 관해 R&D 수행

    • 기존 구축해온 Kafka Cluster를 활용할 수 있는 Kafka Connect가 적합하다고 판단

    • 이와 관련된 내용을 팀원에게 전파하였고 Distributed Mode 기반의 Kafka Connect(Debezium Source Connector, JDBC Sink Connector) 아키텍처를 구축하여 해결함

    • 레거시 DB에서 발생하는 일일 평균 1만 건 이상의 데이터 변경사항을 준실시간으로 신규 DB에 동기화하는 파이프라인을 구축함

Redis Cluster 구축을 통한 원격 캐시의 고가용성(High Availability) 확보 및 모니터링 체계 수립

  • 문제

    • 캐시 데이터의 지속성 및 데이터 정합성 확보 필요

    • 시스템 장애 발생 시에도 서비스 연속성 보장 필요

  • 해결과정

    • 원격 캐시 시스템으로 Memcached와 비교 후 Redis 도입 결정

    • Master/Slave 구조의 Redis Cluster 구축

    • 노드 장애 시 자동 Failover가 가능한 고가용성 환경 구현

    • 모니터링 체계 수립을 통한 안정적인 운영 기반 마련

프로젝트

프로젝트명

수위키 (수원대학교 시간표/강의평가 플랫폼)

소속/기관명

개인

프로젝트 기간

2021.01. ~ 진행 중

프로젝트 내용

개요

회원수 3080명, 게시글 272건, 일간 최대 142,509회의 API 호출을 기록한 시간표/강의평가 서비스

(2022.01 - 운영 중)


기술 스택

Java 17, Spring Boot 3.0, JPA, QueryDSL, MySQL


문제 해결


문제 해결 과정

강의평가 작성 시 발생한 갱신손실 문제 해결 과정

[문제 상황]

수위키는 수강신청 기간에 학우분들의 이용량이 급증하는 특징을 가지고 있습니다. 이 기간에 학우분을 통해 강의평가의 평점 수정이 적용되지 않았음을 제보받았습니다.

그 원인으로 여러 Thread에서 동시에 강의평가 테이블을 Write하는 과정에서 서로 다른 트랜잭션의 충돌로 나중에 커밋한 트랜잭션의 값으로 덮어쓰는 갱신손실 현상이 발생한 것을 파악했습니다.

[해결 과정]

이 문제를 해결하기 위해 DBMS의 Lock, Isolation Level, 각 Isolation Level에서 발생하는 Anomaly를 학습했습니다.

현재 문제 해결에 가장 중요한 수단인 Lock을 적용하는 방법으로, Lock을 Application Level에서 처리하는 Optimistic Lock과

DBMS에 실제로 Lock을 적용하는 Pessimistic Lock이 있음을 학습하고 두 방법의 장/단점을 비교했습니다.

  • Optimistic Lock은 주로 Version Field를 추가하여 Transaction에서 처음 조회되었을때의 Version과 Commit될때의 Version을 비교해 두 버전이 다르다면 이는 충돌이 발생한 것으로 판단하고 예외를 발생시키는 방법으로, 실제 DBMS의 Lock을 사용하지 않기 때문에 비교적 성능이 우수하지만 예외 처리와 롤백을 처리하는 로직을 작성하는 리소스가 소요되는 단점이 있고

  • Pessimistic Lock은 DBMS의 Lock을 활용하기 때문에 충돌을 고려하지 않아도 되는 장점이 있지만 데드락이 발생할 수 있어 가용성이 떨어진다는 단점이 있음을 알게됐습니다.

두 Lock방식의 장/단점과 아래의 두 가지 근거를 바탕으로 Pessimistic Lock을 적용하기로 결정했습니다.

  • 운영 중에 핫라인으로 제보받은 갱신 손실 문제였기 때문에 빠르게 대처해야했습니다.

  • 쓰기 성능보다 데이터 손실의 여지없이 학우분들께 더 신뢰성있는 데이터를 제공하는 것이 우선이라고 판단했습니다.

강의평가를 Update하는 구문에 @Lock(LockModeType.PESSIMISTIC_WRITE)을 적용하여 값을 수정할 때 SELECT FOR UPDATE 구문을 적용할 수 있도록하여 어떤 트랜잭션에서 수정 중이라면 해당 리소스에 접근하지 못하도록 Lock을 적용했습니다.

이 경험을 통해 시스템을 개발할 때 동시성 문제를 고려할 수 있는 견문을 쌓았고, 동시성 문제가 왜 발생하는지 어떻게 해결할 수 있는지 학습했습니다.


홈 API 응답시간 68% 성능 개선 과정

[문제 상황]

학우분들이 사용 하시면서 많은 CS를 남겨주신 내용은 메인 페이지(홈 API)의 로딩속도였습니다.

이 문제를 해결하기 위해 조회 성능을 향상시킬 수 있는 방안을 탐구했습니다.

[해결 과정]

조회 성능을 향상하기 위해선 Cache라는 도구가 있고, 더 나아가 System Local에 보관하는 LocalCache와 원격 시스템에 보관하는 Remote Cache가 있음을 학습했습니다.

이 과정에서 Local Cache로 잘 알려진 오픈소스인 EhCache와 Caffeine Cache를 비교하면서 왜 Caffeine Cache가 더 빠르게 동작하는 것인지 그 원리를 학습했습니다.

  • Caffeine Cache는 TinyLFU알고리즘을 활용하여 캐시 Evict 대상과 Protected 대상을 관리함으로써 빈도를 비교하여 캐시에 등록 여부를 결정하는 원리가 핵심이라는 것을 배웠습니다.

  • 그리고 Cache는 매번 DB I/O를 할 필요가 없는 데이터를 임시로 저장하여 비기능적으로 성능을 향상시키기 위해 사용된다는 것을 알게 되었습니다.

Cache의 가용성을 높이고자 eTag를 활용하여 Cache의 Version으로 클라이언트와 DB I/O를 조율할 수 있는 방안을 마련했습니다.

이 경험을 통해 Cache를 적용하면 왜 조회 성능이 향상될 수 있는지, Cache를 어떤 상황에 적용하는 것이 적절한지 판단할 수 있는 견문을 얻었습니다.

프로젝트명

숏스 (키워드로 보는 짧은 뉴스)

소속/기관명

개인

프로젝트 기간

2023.04. ~ 2024.07.

프로젝트 내용

개요

특정 시간대 뉴스의 내용을 키워드로 요약하여 제공하는 서비스

(2023.04 - 2024.07 (운영종료))


기술 스택

Kotlin 1.7, Spring Boot 3.1, JPA, MySQL, Jsoup, Lucene, Komoran


문제 해결


문제 해결 과정

크롤링 간 데이터 삽입 시 발생한 DB Connection 부족 문제, JPA 영속성 범위를 벗어난 데이터 처리 문제 해결

[문제 상황]

어떤 시기에 데이터가 삽입되지 않은 것을 확인해보니 DB 커넥션이 부족해서 모든 트랜잭션에 롤백되었다는 에러 메세지를 서버에서 확인했습니다.

[해결 과정]

이 문제를 해결하기 위해서 크롤링한 데이터를 삽입하는 로직을 살펴보니 크롤링을 통해 얻은 데이터의 갯수만큼 반복문을 순회하며 쿼리를 보내는 로직이었습니다.

하지만 커넥션을 최소화하여 데이터를 삽입할 수 있는 방법을 알지못해 해결 방법을 알아보기 시작했습니다.

그리고 한 커넥션에서 해결할 수 있는 방법이 벌크 삽입이라고 알게되어 실제 코드로 적용하기로 했습니다.

이 과정에서 JPA, JdbcTemplate, Mybatis 등 다양한 툴이 있는 것으로 알게되어 각 툴을 비교해보기로 했습니다.

우선 현재 JPA의 save()메서드를 반복문만큼 호출하는 것과 saveAll()메서드를 호출하는 것의 성능 비교, 그리고 JdbcTemplate, Mybatis를 활용한 벌크 쿼리의 성능을 비교했습니다.

성능은 Mybatis와 JdbcTemplate이 거의 동일했고 JPA를 사용하는 메서드가 현저하게 떨어진다는 것을 확인했습니다.

개발 편의성이 조금 더 높다고 판단된 JdbcTemplate을 사용하여 벌크 처리를 하였으나 JPA를 사용하지 않기 때문에 영속성에 해당되지 않기 때문에 JPA가 매핑하는 AutoIncrement가 적용된 PK를 인식하지 못하는 현상이 발생했습니다.결국에는 id는 null 값이 들어간 상태가 된 아주 문제가 큰 상태가 되었습니다.

이 문제를 해결하기 위해 직접 PK를 다루는 부가적인 로직이 추가하여 해결했습니다.

  1. 데이터 삽입 전 현재 DB에 있는 가장 마지막 인덱스를 뽑아온다.

  2. 데이터를 삽입 후 가장 마지막의 인덱스를 뽑아온다.

  3. 1번과 2번의 사이에 해당하는 모든 수를 추출하여 Wrapper Entity에서 보관하고 사용한다.

위 로직을 코드로 녹여내어 하나의 커넥션에서 약 800 ~ 1200개의 객체를 DB에 삽입할 수 있도록 개선했습니다.

성능 이슈 발생 시 로그 확인부터 시작하여, 다양한 해결책을 시도해보고, 각 방법의 장단점을 비교 분석하고자 하는 자세와 대용량 데이터 처리 시 단순히 코드가 동작하는 것을 넘어 성능과 리소스 관리를 고려한 설계가 필요하다는 것을 배웠습니다.


크롤링 스케줄링 중 발생한 OOM 문제 해결 과정

[문제 상황]

숏스는 정각마다 발행되어있는 뉴스를 크롤링한 후 유사한 뉴스들의 내용을 키워드로 요약하여 제공하는 서비스입니다.

2Core, 2GB Memory 스펙을 가진 시스템에서, 스케줄링 타이밍이 지났음에도 DB에 서비스 데이터가 적재되지 않아 에러 로그를 확인해보니 OOM문제가 발생하고 있었습니다.

[해결 과정]

첫 번째 해결방안으로는 Heap 할당 수치를 조정하기로 했습니다. 로컬 환경에서 크롤링 로직이 요구하는 Heap크기는 약 2GB임을 확인한 후

하지만 이 방법은 Heap크기를 2GB로 할당한다면 서버 하드웨어의 최대 메모리만큼 할당하게 되는 것이고 이 때문에 시스템 전체가 다운될 수 있는 위험이 있어 적용하지 않았습니다.

두 번째 해결방안으로는 Heap Dump를 확인하여 코드 내에서 메모리 최적화를 할 수 있을지 판단하는 것이었습니다. Heap Dump를 통해 정의한 문제는 “Bulk Insert과정에서 약 1000개가 넘는 객체를 스케줄링 타이밍 동안 가지고 있어 GC 후보로 선정될 수 없기 때문”인 것이었습니다.

이를 해결하기 위해서 약 1,000개의 데이터를 한 번에 Bulk Insert를 수행하던 로직을 100개씩 분할하여 Bulk Insert할 수 있도록 개선했습니다.

하지만 이 방법으로도 여전히 간헐적인 OOM에러가 발생하게 되면서 또 다른 해결방안을 적용했습니다.

세 번째 해결방안으로는 물리 메모리 4GB를 가진 시스템으로 Scale-Up 하는 것이었습니다. 큰 비용을 투자한 만큼 가용성을 확보하기 위해 적절한Heap Size와 GC를 선정하는 R&D를 수행했습니다. JVM의 Heap할당 기본 옵션을 사용하지 않도록 java -Xms256m -Xmx2048m명령어 옵션을 적용했고, 효율적인 메모리 관리를 위해 Oracle 공식문서를 기반으로 Serial, Parallel, G1GC를 비교분석하여 현재 시스템의 기본값으로 지정된 GC인 SerialGC가 아닌 -XX:+UseG1GC명령어 옵션으로 G1GC를 선정했습니다.

이 경험으로 메모리 추적을 위해 Heap Dump를 확인하는 방법과 메모리 관리의 중요성을 배웠고

GC, JVM 튜닝에 관한 공식문서를 읽으며 JVM 환경에 더 깊은 이해를 얻었습니다.


약 150만건의 데이터를 가진 테이블 내 조회 시 90% 성능 개선 과정

[문제 상황]

숏스의 뉴스를 적재하는 테이블은 시간당 약 1,000건의 데이터가 추가되고 있었고 시간이 갈수록 조회 성능이 떨어지게 되는 점이 문제점이었습니다. 실제로 뉴스 테이블 내에서 특정 키워드가 포함되고, 특정 기간 사이에 해당하는 뉴스를 조회하는 질의를 수행했을 때 응답까지 약 13초가 소요되는 문제가 발생했습니다.

[해결 과정]

첫 번째 해결방안으로는 Cache를 적용하여 질의문의 결과를 임시로 저장하는 것이었습니다. 하지만 이 방법은 캐시에 없는 내용을 요청하게 되면 개선 전과 마찬가지로 응답까지 긴 시간을 기다려야한다는 문제가 남아있어 적절한 해결방안이 아니라고 판단했습니다.

두 번째 해결방안으로는 데이터베이스 자체의 조회 성능을 최적화하는 방법을 찾는 것이었습니다. 가장 잘 알려진 방식인 Index를 적용하여 조회 성능을 최적화하기로 결정했습니다.

이 때 Index가 어떤 것이고, 어떤 원리로 조회 성능을 최적화할 수 있는지 등 Index에 관한 세부적인 내용을 학습했습니다.

  • 첫 번째로, MySQL기준으로 인덱스는 B-Tree 기반의 자료구조로 구성되어있고 조회 조건을 Key로하여 해당하는 노드만을 탐색하는 방식으로 순회하기 때문에 조회에 빠른 성능을 제공할 수 있었음을 학습했습니다.

  • 두 번째로, 삽입/삭제가 빈번하게 일어나는 테이블에는 적절하지 않다는 점입니다. 새로운 인덱스를 추가하거나, 레코드가 추가되면 그 내용을 반영해야 하는 특징이 있기 때문에 이에 대한 오버헤드가 발생하기 때문입니다.

  • 세 번째로, 인덱스는 일반적으로 10% ~ 20%의 저장공간을 추가로 요구하기 때문에 조회 성능을 극대화하고자 다수의 Index를 생성하는 것이 적합하지 않을 수 있다는 점을 학습했습니다.

  • 네 번째는, Index에 적합한 컬럼은 Cardinality가 높아야하는데, Cardinality가 높으면 검색 대상이 줄어들기 때문이라는 점을 학습했습니다.

이 모든 내용을 고려하여 뉴스 테이블 내의 created_at컬럼을 보조 인덱스로 지정했습니다.

인덱스를 지정한 내용을 JPA레벨에서도 적용하기 위해 @Table애노테이션의 옵션으로 ORM과 DBMS의 Index싱크를 맞추는 것으로 해결책을 적용했습니다.

이 경험으로 DBMS의 조회 성능 최적화를 위한 방법과 인덱스를 사용할 때 고려해야하는 요소들을 배웠습니다.


교육

소속/기관명

수원대학교

종류 | 전공

대학교(학사) | 정보보호학과

재학 기간 | 재학 상태

2018.03. ~ 2024.02. | 졸업

대외활동

활동명

IT 개발 동아리 Mash-Up Spring Team 13기 ~

소속/기관명

Mash-Up

연도

내용
댓글