💁♂️ About me
깊게 고민하고 함께 개발합니다.
사용자에게 사랑받는 좋은 소프트웨어는 팀의 원활한 커뮤니케이션과 깊은 고민이 담긴 유연하고 견고한 코드로부터 시작되는 것이라 생각하며, 높은 품질의 소프트웨어와 최적의 사용자 경험을 위해 팀이 함께 고민하고 문제를 헤쳐 나가는 건강한 협력을 즐깁니다.
빠른 실행력과 실패를 통해 더 나아갑니다.
운영 중인 서비스에서 사용자 경험을 효율적으로 개선할 수 있었던 이유는 빠르게 아이디어를 구상하고 간단한 파일럿 프로젝트를 개발하며 겪은 여러 실패 경험 덕분이라고 생각합니다. 이같이 실패를 두려워하지 않고 부딪혀 나아가는 힘은 함께하는 팀의 성장과 사용자 경험 개선에 꼭 필요한 원동력이라고 믿으며 개발합니다.
꾸준함을 바탕으로 한계 돌파를 즐깁니다.
팀의 성장이나 건강한 커뮤니케이션, 사용자 경험 최적화를 위해 개인의 한계를 뛰어넘는 것은 필수 불가결이라고 생각합니다. 이러한 가치를 바탕으로 다양한 기술에 대해 관심을 가지고 꾸준히 학습해 한 단계 나아가고자 노력합니다.
🛠️ Skills
- Kotlin / Java
- Spring Framework / Spring MVC / Spring Data JPA / Spring Security / Spring Rest Docs
- JPA / Querydsl
- JUnit5 / Kotest / MockK
- Gradle
- AWS EC2 / AWS SQS / AWS SES / AWS S3
- AWS ElastiCache for Memcached / AWS Aurora for MySQL
💡 Work Experience
인사이트 서비스 설계 및 개발
프로젝트 기간
- 2023.01 ~ 2023.04
개발 환경
- Kotlin 1.8.20 Gradle 8.0.2
- Spring Boot 3.0.2 Spring Data JPA Spring Security Spring REST Docs Querydsl Blaze Persistence Kotest MockK
- AWS Aurora for MySQL AWS Elasticache for Memcached
인사이트 피드 조회 쿼리 개선
인사이트 도메인은 유저, 입점 제품, 직접 등록 제품, 카테고리, 키워드, 댓글, 북마크 등 여러 도메인과 릴레이션 테이블 간에 복잡한 관계를 맺고 있습니다. 이러한 복잡한 관계에서 JPA Mapping을 그대로 사용할 경우 단일 조회는 1 + N 만큼, 한 번에 여러 인사이트를 조회해야 하는 피드 조회는 M * N 만큼 쿼리를 호출하며, 심각한 경우에는 Cross Join 쿼리를 호출해 불필요한 자원을 낭비했습니다.
이러한 문제를 해결하기 위해 인사이트 도메인과 연관관계를 맺는 여러 도메인에 정규화/반정규화 작업을 진행하여 역할이 비슷한 도메인 별로 묶어 Inner Join을 통해 한 번에 조회할 수 있도록 구조를 정리했습니다.
또한, QueryDsl의 @QueryProjection과 transform() 함수를 적극 활용하여 Join 쿼리를 요청할 때, 조회 대상이 되는 Entity의 모든 데이터를 조회하지 않고 응답 모델에 필요한 데이터만 조회하여 쿼리 응답에 대한 부하를 줄일 수 있도록 했습니다.
이러한 방법을 통해 최종적으로 M * N 만큼 호출하는 쿼리의 수를 릴레이션 테이블 페이징 쿼리, 유저 관련 쿼리, 제품 관련 쿼리, 인사이트 관련 쿼리로 나누어 총 4번의 호출만으로 인사이트 단일 조회 및 피드 조회에 필요한 모든 데이터를 조회할 수 있도록 개발했습니다.
그 결과, 응답 대기 시간을 약 100ms ~ 150ms 으로 줄일 수 있게 개선하여 유저에게 더 빠르게 정보를 제공할 수 있도록 했습니다.
인사이트 및 키워드 검색 기능 개발
기존 이커머스 서비스인 먼슬리씽 스토어의 제품 검색 기능은 Like 검색을 사용해 구현되었습니다. 이 방식은 제품명을 기준으로 설정하고 인덱싱 후 검색하는 것으로, 비교적 큰 무리 없이 서비스를 운영할 수 있었습니다.
그러나, 제목 / 카테고리 / 키워드 / 본문 내용 등 여러 데이터를 통합하여 검색해야 하는 요구사항을 충족하기 위해 기존 Like 검색 방식을 사용할 경우 모든 컬럼의 데이터를 순차적으로 순회하며 검색어가 포함된 경우를 찾아야 하기 때문에 복잡한 검색어를 입력했을 때 많은 자원을 사용하는 것과 동시에 검색 품질이 떨어지는 문제를 야기해 고객 경험을 저하시킬 수 있다고 생각했습니다. 또한, 촉박한 일정 안에 검색 기능을 구현하기 위해서는 최대한 적은 러닝 커브로 개발을 진행해야 했습니다.
이와 같은 여러 상황을 고려했을 때, 지정한 컬럼의 데이터를 특정 길이만큼 잘라 인덱싱하여 복잡한 검색어가 입력되어도 높은 수준의 검색 품질을 유지할 수 있는 MySq l의 N-Gram Parser 를 적용해 검색 기능을 구현하도록 했습니다.
다만 한국어를 기준으로 N-Gram Parser 의 인덱싱 기능을 사용할 경우, 영어에 비해 성능이 떨어질 수 있으나, 단어 기준으로 텍스트를 나누어 인덱싱하기 때문에 Like 검색에 비해 월등히 빠른 성능과 검색 품질로 유저 경험을 향상시킬 수 있을 것으로 판단했습니다.
이를 통해 추가적인 기술 도입 없이 검색 기능을 정해진 기간 내에 구현할 수 있게 되었고, 결과적으로 검색 쿼리의 성능을 약 15% 향상시킬 수 있는 좋은 결과를 얻을 수 있었습니다.
쿠폰 도메인 설계 및 개발
프로젝트 기간
- 2022.10 ~ 2022.11
개발 환경
- Java 8 Gradle 6.2
- Spring Boot 1.5.13 Spring Data JPA Querydsl JUnit5
- Vue 2
- AWS Aurora for MySQL
기존 쿠폰 등록 로직 리팩토링
기존 쿠폰 도메인은 하나의 코드를 기준으로 설정한 인원수만큼만 쿠폰을 다운로드할 수 있도록 테이블이 설계되어 있었습니다. 그러나 스케일 아웃을 고려하지 않고 개발되어 유저가 악의적으로 중복으로 등록하거나 설정한 인원수를 초과하는 등 서비스 운영에 심각한 문제를 가지고 있었습니다.
쿠폰 등록 시 발생하는 문제 중 하나는 동시성 처리에 대한 문제입니다. 쿠폰 등록 API를 호출할 때 유효성 검증을 위해 현재 등록된 인원수 조회 쿼리를 처리한 후 쿠폰을 등록하는 과정에서, 이보다 먼저 호출한 쿠폰 등록 API의 처리에 의해 제한된 등록 인원수에 도달했으나 해당 값을 초과하여 등록되는 문제가 발생했습니다.
이를 해결하기 위해 MySQL에서 제공하는 Low Rock 기능을 활용하여 현재 쿠폰 등록을 위해 처리 중인 트랜잭션이 있을 경우 다른 트랜잭션에서 접근할 수 없도록 하여 제한된 인원수를 초과하는 문제와 중복 쿠폰 등록 등의 여러 동시성 문제를 해결할 수 있었습니다.
다양한 역할의 쿠폰 도메인 설계 및 개발
기존 쿠폰 도메인은 다양한 프로모션에 적용하거나 유저들에게 더 다양한 종류의 쿠폰을 제공하기 어려운 구조로 설계되어 있었습니다. 이러한 문제로 인해 새로운 고객을 유치하고 기존 유저들이 먼슬리씽 스토어를 꾸준히 사용하기에는 매력적이지 않다고 판단했습니다.
이러한 사업적 문제를 해결하기 위해 카드사 전용 쿠폰, 장바구니 쿠폰, 배송비 쿠폰, 묶음 배송 쿠폰 등 여러 역할의 쿠폰 도메인을 새롭게 설계하고, 이를 기존 쿠폰 테이블과 연계하여 운영할 수 있도록 정규화/반정규화 과정을 거쳐 사업적으로 더 확장성 있는 쿠폰 서비스를 제공할 수 있도록 했습니다.
적립금 도메인 설계 및 개발
프로젝트 기간
- 2022.09 ~ 2022.10
개발 환경
- Java 8 Gradle 6.2
- Spring Boot 1.5.13 Spring Data JPA Querydsl JUnit5
- Vue 2
- AWS Aurora for MySQL
적립금 도메인 테이블 구조 재설계
기존 적립금 도메인 구조는 지급된 적립금이 어떻게 사용되었는지 추적이 불가능하여 부분 취소 / 반품 비즈니스 로직에 대해 유연하게 대응하기 어려운 문제가 발생했습니다.
이에 따라 적립금 내역 테이블과 해당 내역의 처리 로그 테이블 간 연관관계를 가지도록 자기참조 테이블로 설계하여 적립금 히스토리를 추적할 수 있도록 테이블 구조를 정규화/반정규화 작업을 통해 재설계했습니다.
또한, 자기참조 관계를 가지는 테이블 구조로 인해 연결성을 가지는 참조 컬럼에 인덱스를 설정하여 Full Scan 으로 인한 성능 저하를 방지하도록 설계했습니다.
결과적으로 모든 사용자의 적립금을 추적할 수 있게 되어 적립금과 관련된 여러 복잡한 요구사항을 수월하게 처리할 수 있도록 하여 서비스를 원활하게 운영할 수 있도록 기여했습니다.
적립금 내역 별 잔여 금액 조회 기능 구현
기존 적립금 도메인은 각각의 적립금 내역에 대한 잔여 금액을 알 수 없어, 이벤트 및 프로모션 보상의 다양한 요구사항을 반영하는 것이 제한적인 문제가 발생했습니다.
이에 따라 잔여 적립금 계산 시, 적립금 내역 테이블과 처리 로그 테이블 조회 쿼리에 Join, Group By, 집계 함수를 사용하여 애플리케이션 계층의 로직 처리에 대한 책임을 줄일 수 있도록 개발했습니다.
이를 통해 보다 다양한 이벤트 및 프로모션 보상 요구사항을 반영할 수 있도록 개선했습니다.
유효기간 부여 기능 및 자동 소멸 기능 구현
적립금 추적 및 각 적립금 내역 별 잔여 금액 조회가 불가해 지급된 적립금이 지속적으로 쌓이는 문제가 발생 했습니다.
이를 해결하기 위해, 적립금 도메인에 사용 가능일자와 소멸일자 속성을 추가하고 이를 기준으로 스케줄러를 구현하여 적립금 소멸 자동화 프로세스를 구현했습니다.
이를 통해 지속적으로 적립되는 적립금이 쌓이는 문제를 해결할 수 있었습니다.
주문 / 결제 도메인 구조 및 성능 개선
프로젝트 기간
- 2022.06 ~ 2022.09
개발 환경
- Java 8 Gradle 6.2
- Spring Boot 1.5.13 Spring Data JPA Querydsl JUnit5
- Vue 2
- AWS Aurora for MySQL AWS SQS
주문 테이블 반정규화 및 주문 / 결제 / 재고 처리 프로세스 성능 개선
기존 주문 처리 프로세스는 하나의 트랜잭션에서 모든 도메인의 역할을 동기적으로 수행하고 있어, 유저 응답 대기 시간이 대단히 길어지는 문제가 발생했습니다. 이러한 문제를 해결하고자, 다음의 여러 가지 개선 작업을 시행했습니다.
첫째로, 기존의 주문 테이블을 도메인 기준으로 반정규화 작업을 진행하여 분리했습니다. 이를 통해 주문, 결제, 재고 처리 도메인을 각각의 트랜잭션으로 분리할 수 있었습니다. 또한, 비즈니스 로직을 개선함으로써 기존 주문 처리 프로세스의 결합도를 낮추면서 각 모듈 별 단일 책임 원칙을 지킬 수 있도록 리팩토링 했습니다.
둘째로, 재고 처리 프로세스의 경우 AWS SQS 를 도입하여 결제에 성공 했을 때 PG 사의 웹훅을 Message Queue 에 담아 순차적으로 재고를 처리하도록 구현 했습니다. 이와 동시에, 유저는 재고 처리와 관계없이 결제 처리 여부만 확인할 수 있도록 구현하여, 주문 / 결제 유저 응답 대기 시간을 대폭 개선할 수 있었습니다. 이를 통해 유저 응답 대기 시간은 약 4500ms 에서 800ms 으로 감소시켜 주문 처리 프로세스의 효율성을 대폭 개선할 수 있도록 했습니다.
주문 페이지 응답 속도 개선
주문 페이지 응답 속도 개선 작업도 수행해 유저 경험을 향상시킬 수 있도록 했습니다.
이전에는 주문 페이지 진입 시 제품 및 할인 정보, 판매처 정보 등 여러 데이터들을 서브 쿼리로만 이뤄진 하나의 쿼리로 조회해 화면 렌더링까지 약 2000ms 소요되는 문제가 발생하였습니다.
이를 개선하기 위해 서브 쿼리로만 구성된 하나의 쿼리를 주문 / 제품 / 이벤트 및 프로모션 등 각 도메인 역할 별로 함수를 분리하고 각각의 트랜잭션에서 처리할 수 있도록 쿼리를 개선하였습니다.
이에 더해, 더 많은 기능을 추가하여 유저들이 더욱 쉽게 주문할 수 있도록 리팩토링을 진행해 주문 페이지 진입 시 제품 및 할인 정보, 판매처 정보뿐만 아니라, 상품 리뷰, 유저들의 구매 이력 등 다양한 정보들을 빠르게 확인할 수 있도록 했습니다.
이러한 리팩토링 과정을 통해 주문 페이지 렌더링 소요 시간을 약 2000ms 에서 500ms 으로 개선 했습니다.
어드민 시스템 개발 및 유지보수
프로젝트 기간
- 2021.01 ~ 2022.06
개발 환경
- Java 8 Gradle 6.2
- Spring Boot 1.5.13 Spring Data JPA Querydsl JUnit5
- Vue 2
- AWS Aurora for MySQL
어드민 2.0 업데이트 및 파트너 사 어드민 개발
기존 레거시 프로젝트에서는 테이블 구조가 복잡하고 확장하기 어려워 높은 수준의 요구사항을 반영하는 것이 어려웠습니다. 그러나 이 문제를 해결하기 위해, 운영 중인 서비스의 모든 도메인을 다시 분석하고 정규화 / 반정규화 작업을 통해 테이블 구조를 재설계하고 데이터 마이그레이션 작업을 수행했습니다. 이를 통해, 프로젝트의 기존 요구사항을 개선하고 새로운 기능을 추가하는 데 더 많은 유연성을 제공할 수 있게 되었습니다.
또한, 프로젝트의 MVP 단계부터 참여하여 주도적인 커뮤니케이션을 바탕으로 의사결정을 수행했습니다. 이를 통해, 팀 간 협력을 향상시키고 프로젝트의 성공을 보장할 수 있었습니다.
이외에도, 기존 Mutache Template으로는 구현하기 어려웠던 복잡한 요구사항을 Vue 2 프레임워크를 도입하여 조금 더 유연하게 화면 렌더링을 구현할 수 있도록 어드민 시스템을 리팩토링하고 파트너사 어드민 시스템을 신규 개발하여 사용자 경험을 향상시키고 효율성을 높였습니다. 이러한 노력들은 프로젝트의 성공과 지속적인 성장에 큰 영향을 미쳤습니다.
🏃♂️ Personal Experience
B-Side 14th
- 건강한 커뮤니케이션 스킬 향상
- 안정적인 소프트웨어를 위한 TDD, BDD, Spring REST Docs 도입
- 실무에서 경험할 수 없었던 기획자, 디자이너, 개발자가 모여 하나의 공통된 목표를 향해 서비스를 만들어가는 과정 경험
- 주도적으로 전 과정에 참여하고 MVP 스펙, 와이어 프레임, R&R, 플로우 차트 등 여러 산출물을 바탕으로 아키텍처 설계 및 일정 별 업무 계획 등 체계적으로 개발
- 사내 팀원 외의 개발자들과 함께 협업하며 팀원을 설득하는 방법과 의견을 받아들이는 방법 등 커뮤니케이션 스킬 함양
- Spring REST Docs 를 Kotlin DSL 스타일로 커스텀하여 테스트 코드를 작성할 수 있도록 구현
📚 Education
NEXTSTEP TDD 14th
- OOP 에 대한 전반적인 개념에 대해 학습하고, 기존 개발 습관이 어떻게 잘못 되었는지 파악 할 수 있었습니다.
- TDD 를 직접 경험해보면서 이를 OOP 와 연결하는 사고력을 키울 수 있었습니다.
- JUnit5 를 경험하며 테스트 코드의 중요성에 대해 깨닫고 실무에서 적용해 볼 수 있는 힘을 기를 수 있었습니다.
F-Lab Mentoring
- 학습한 내용을 블로그에 기록하며 이해한 내용이 무엇이고 그렇지 못한 내용은 무엇이며 어떻게 질문해야 하는지 등 효율적으로 학습하는 방법에 대해 경험 했습니다.
- 멘토링을 통해 학습한 내용에 대해 발표하고 이에 대해 피드백을 받으며 좋은 커뮤니케이션이란 무엇인지 몸소 느낄 수 있었습니다.
- 실무에서 작성한 코드가 어떻게 동작 하는지, 어떤 디자인 패턴이 적용 되었고 프로세스와 스레드에서는 어떻게 작업을 처리 하는지 등 CS 와 Java, Spring Framework 에 대해 깊이 학습 할 수 있었습니다.
KH 정보교육원 수료
- 꾸준한 예•복습으로 학급 내 성적 최우수상을 수상 했습니다.
- Java, Oracle, Spring Framework, JSP 등의 기술을 사용해 두 번의 프로젝트를 진행하며 웹 기술 및 통신에 대한 기본적인 지식을 학습 할 수 있었습니다.
- 주도적인 성격과 빠른 이해력으로 팀 리더를 맡아 프로젝트 리딩을 경험 했습니다.
서경대학교 뮤지컬학과 졸업
- 2013.03 ~ 2019.02