미리보기
기본 정보
프로젝트의 성능을 향상 시킬 방법이 무엇인지, 내가 사용하고 있는 방식이 최선일지, 내 코드는 동료들이 읽기에 편한지에 대한 생각을 많이 합니다.
기술 스택
Spring Boot, Java, MySQL, querydsl, spring-jpa
경력
주식회사 유니온클래스
사원 | 개발팀 | 재직 중
2024.02. ~ 재직 중 (11개월)
신규 서비스 런칭에서 기획, 설계, 인프라, 백엔드 개발을 담당하고 있습니다
'Untitled'를 개발 및 운영중입니다. (https://untitled-shorts.com)
포트폴리오
프로젝트
반려동물 제목 추천 서비스 Untitled
주식회사 유니온클래스
2024.02. ~ 진행 중
유저가 업로드한 반려동물 사진에 대해 생성형 AI가 알맞은 제목을 생성해주는 Shorts-Image Platform입니다.
기획, ERD설계, Shorts 도메인 API 개발, 전체 도메인 유지/보수 및 필요한 API 개발, Infra 및 CI/CD 구축 을 담당했습니다
주요 개발 사항
Spring Event를 사용하여 도메인간 의존성 분리
[문제 원인]
- 한 번의 Request에서 서로 다른 도메인에서의 비즈니스 로직이 필요한 경우가 있음
- 이 경우 Controller나 Service에서 다른 도메인을 의존하는 문제가 발생
[해결 과정]
- 각 도메인에서는 Spring Event를 발행만 시켜주고, EventHandler에서만 의존성 결합을 허용하여 해결
[성과]
- 각각 도메인에서 다른 도메인의 의존성을 완전히 분리
ㅤ
Facade Pattern으로 복잡성 감소 및 도메인과 객체간 의존성 분리
[문제 원인]
- 서비스가 커지면서 하나의 service가 여러 객체를 의존하게 됨
- 따라서 객체간 결합도가 높아지고 클래스 하나의 복잡성이 너무 커져, 유지/보수 및 테스트 코드 작성이 힘들어짐
- 또한 Response가 필요한 부분에서는 Spring Event를 사용할 수 없는 문제 발생
[해결 과정]
- Facade Pattern을 사용하여 Facade 클래스에서만 다른 도메인간의 의존성 결합을 허용함으로써 Spring Event를 대체
- 같은 도메인이더라도 여러 개의 Service나 Repository를 의존하는 경우, Facade 클래스로 분리함으로써 복잡성을 줄임
[성과]
- 응답값이 필요한 로직에서의 도메인간 의존성을 완전히 분리
- 거대한 클래스의 복잡성을 줄여서 유지/보수와 테스트 코드 작성이 쉬운 코드로 리팩토링
ㅤ
DIP를 적용하여 계층 간 의존성 분리
[문제 원인]
- 기존 레이어드 아키텍처에서는 Service 계층에서 JPA Repository를 곧바로 호출하여 사용
- 이는 상위 모듈이 하위 모듈에 의존하기에 DIP를 위반하고, 주요 로직이 DB에 의존적이게 된다는 문제 발생
[해결 과정]
- Service 계층과 Repository 계층 사이에 중간 계층 역할을 해주는 Interface를 생성하고, Service와 Repository에서 Interface를 의존하게 만듦으로써 의존성 방향을 뒤집음
[성과]
- 확장에 용이하고, 계층간 의존성이 분리된 코드로 리팩토링
- DB 의존성을 떼어내고 통합 테스트를 단위 테스트로 변경하여 테스트 속도 향상(대략 62.85% 향상)
ㅤ
GitHub Actions를 사용하여 CI/CD 구현
[문제 원인]
- Jenkins를 사용하여 CI/CD를 진행하고 있었는데, 메모리를 너무 많이 사용해서 프론트/백이 동시에 github에 push를 하면 서버가 다운되는 상황 발생
- Jenkins용 프리티어 EC2를 생성하려 했지만, 프리티어라 속도가 느리고 elastic ip비용 + ebs 비용이 발생한다는 문제가 존재
[해결 과정]
- Github Actions를 사용하여 CI/CD 구축
[성과]
- 추가 비용 없이 서버가 다운되는 현상을 예방
ㅤ
무분별한 외부 API 사용을 제한하기 위해 Rate Limiter 도입
[문제 원인]
- 서비스의 핵심 기능에서 유료 API를 사용하고 있어서 핵심 기능 API 호출에 제한을 걸어야 함
[해결 과정]
- Bucket4j를 사용해서 특정 API에서 유저의 당일 호출 횟수를 기록하고, Filter에서 Limit을 넘기면 Exception을 발생하도록 설정
[성과]
- 유저의 무분별한 API 호출을 제한하여 서비스 운용 비용 절감
ㅤ
정렬된 Cursor 기반 페이지네이션 사용
[문제 원인]
- 서비스에서 조회가 필요한 곳에 페이지네이션을 적용시키는데 성능 향상을 위해서 No-Offset 방식을 사용
- 이때 DB에는 데이터가 id 오름차순으로 저장되는데, 실제 요구사항은 id가 아닌 다른 컬럼으로 정렬돼야해서 Cursor를 다른 값으로 사용해야 함
[해결 과정]
- Cursor를 '정렬대상컬럼 + id' 로 커스텀하여 원하는 값으로 정렬된 무한스크롤 구현
[성과]
- id가 아닌 다른 값으로 정렬해야하는 상황에서도 cursor-based pagination을 적용하여 성능 향상
ㅤ
다형성을 활용하여 재사용성 높은 코드 작성
[문제 원인]
- 똑같은 비즈니스 로직이지만 입력받는 파라미터만 다른 중복된 메서드들이 존재
[해결 과정]
- 파라미터들을 하나의 공통된 인터페이스의 구현체로 만듦
- 메서드에서는 '<T extends 인터페이스> 메서드(T t)' 와 같은 형태로 파라미터를 받아서 해결
[성과]
- 중복된 코드 제거를 통해 가독성 향상 및 공통된 부분을 하나로 묶음으로써 코드 재사용성 증가
- 수정이 필요할 때 메서드 하나만 수정하면 되므로 유지/보수성 향상
ㅤ
LogBack을 사용하여 Logging 처리
[문제 원인]
- 실제 서비스를 운영할 때, 예기치 못하게 서버가 다운되거나 ci/cd 워크플로우가 동작하면 서버의 log가 날라가는 문제 발생
[해결 과정]
- LogBack을 사용하여 Info와 Error로 출력되는 로그들을 분리하여 날짜별로 저장
[성과]
- 에러가 발생했을 때 저장된 로그를 확인함으로써 원인 추적 가능
ㅤ
'좋아요' 기능에 캐싱 적용
[문제 원인]
- 기존에 '게시글의 좋아요 수'가 필요할때는 '좋아요' 테이블의 개수를 조회해서 사용
- 이 경우 매번 count 쿼리를 날려야 하므로 비효율적
[해결 과정]
- '게시글' 테이블에 '좋아요 수' 컬럼을 추가해서 count 쿼리를 쓰지 않도록 변경
- '좋아요/취소' 행위가 일어날 때마다 캐시에 변경된 값을 업데이트하고, 5분 간격으로 캐시의 값과 DB 값을 동기화 시켜줌으로써 쿼리 빈도를 줄임
[성과]
- count 쿼리를 사용하지 않도록 수정하고, 캐싱을 사용하여 DB connection도 줄임으로써 성능 향상
추가 계획
슬로우 쿼리 파악하여 커버링 인덱스 적용
통합 계정을 위한 Single-Sign-On 구현 및
DB 동기화를 위한 메시징 큐 도입
대외활동
2024 지역사회 문제 해결을 위한 연합 해커톤(부산대학교 LINC 3.0 사업단 주최)
개인
전통시장 과일 시가 정보 제공 플랫폼을 개발하여 특별상 수상
교육
부산대학교
대학교(학사) | 물리학과
2017.03. ~ 2024.02. | 졸업
신세계아이앤씨 Spharos Academy
사설 교육
2023.07. ~ 2023.12. | 졸업
자격증
OPIC
IM2 | ACTFL
2024.03.