이력서 기반의 면접 예상질문 자문자답
이력서 내용 중 일부
📜 주요 프로젝트 경험
콘텐츠 플랫폼 전면 개편 및 운영 효율화
- 대규모 레거시 시스템의 API 구조를 DTO 중심 정적 타입 기반으로 재설계하고, 유효성 검증 계층을 도입하여 시스템 안정성과 감사 추적 가능성을 확보함.
- 시퀀스 기반 키 생성 병목 문제를 해결하기 위해 낙관적 락 기반의 캐시 선점 구조 설계 → 트랜잭션 처리 속도 5배 향상 (1m 30s → 500ms)
- 도메인 중심 API 통합 및 불필요한 호출 제거 → 초기 로딩 시간 56% 개선 (5.2s → 2.3s)
- 과도한 트랙 데이터 조회 문제를 쿼리 범위 제한 및 필드 필터링으로 최적화 → 운영 리소스 1.5MM 절감
글로벌 콘텐츠 유통 시스템 구축
- RBAC 기반 접근 제어와 이벤트 기반 전송 기능을 포함한 유통 플랫폼 백엔드 개발
- DTO 매핑 및 아키텍처 검증 도구 도입으로 코드 일관성과 품질 향상
→ SonarCloud 위반 건수 480건 → 75건 (약 84% 감소)- 전체 다운로드 요청의 성능 병목을 해결하기 위해 조건 기반 예측 모델 및 캐시 분기처리 적용
→ API 서버 CPU 사용률 peak 85% → 40% 감소대용량 Excel 배치 성능 최적화
- 대규모 엑셀 다운로드/업로드 시 발생하는 OOM과 GC 병목을 해결
- POI Streaming 도입 및 청크 조회 기반 쿼리 최적화 적용
→ 메모리 사용량 85% 절감 (1.2GB → 180MB)- Flush & Clear 주기 조정 및 GC 튜닝으로 JVM 안정화
→ 힙 메모리 peak 3.8GB → 1.6GB (약 58% 절감), OOM 발생 0건대량 데이터 등록 병렬 처리 및 락 경쟁 해소
- 80만 건 이상의 데이터를 월 단위로 일괄 등록하는 배치 성능 개선
- 분산 락과 병렬 처리 도입으로 정합성 보장 및 처리 시간 단축
→ 삭제 성능 24배 개선 (2시간 → 5분), 등록 시간 70% 단축 (40분 → 12분)오디오 콘텐츠 제작 플랫폼 구축 및 인증 인프라 개선
- 트랜잭션 커밋 이후 외부 API 호출을 보장하는 이벤트 기반 구조 설계 (
@TransactionalEventListener
)- Redis 기반 인증 세션 관리 및 TTL 설정으로 분산 환경에서 인증 안정성 확보
- ISMS 대응을 위한 JSON 기반 로그 포맷 정비 및 Kibana 대시보드 구축
→ ISMS 인증 3회 연속 심사 통과
예상 질문
공통 질문 (전체 프로젝트 기반)
Q. 사용했던 Java 버전 중 Java 21의 주요 기능은 무엇이며, 어떻게 활용하셨나요? A. Java 21에서는 Record Patterns, Virtual Threads, Sequenced Collections 등의 기능이 도입되었습니다. 이 중 Virtual Thread는 Blocking I/O 처리 성능을 개선할 수 있어 대규모 비동기 작업의 부하 분산에 유용하며, 테스트 환경 및 비동기 이벤트 리스너 구현에서 적용을 검토했습니다. Java 21 Features
Q. Spring Boot 3.2.2
로 전환하면서 겪은 주요 이슈나 마이그레이션 포인트는 무엇이었나요? A. Jakarta EE 10으로 전환되며 javax.*
→ jakarta.*
네임스페이스 변경이 있었고, 서드파티 라이브러리 호환성 이슈가 발생했습니다. 또한 Spring Security 구조 변경으로 인해 기존의 WebSecurityConfigurerAdapter
폐지 대응이 필요했습니다.
Q. Package by Component
구조의 장점은 무엇이고, 어떤 방식으로 적용하셨나요? 기능 중심으로 패키지를 구성하여 관련 로직(Controller, Service, Repository, DTO 등)을 하나의 컴포넌트 단위로 관리합니다. 이를 통해 코드 응집도는 높이고 변경 영향 범위는 줄이며, 신규 기능 추가 시 전체 구조에 미치는 영향을 최소화했습니다.
Q. @TransactionalEventListener
를 도입하신 이유는 무엇이며, 실제 어떤 문제를 해결했는지 설명해주세요. A. 트랜잭션 커밋 이후 외부 API 호출이 필요했던 경우, 이벤트를 트리거로 비동기 처리를 함으로써 롤백 시 부정합을 방지했습니다. 트랜잭션이 완료되기 전에 메시지 큐를 전송하거나 외부 API를 호출하는 문제를 사전에 방지하기 위한 목적이었고, 이를 통해 API 호출의 시점 제어와 트랜잭션 일관성 유지가 가능했습니다.
Q. ArchUnit
, SonarCloud
를 사용한 코드 품질 관리 방식은 구체적으로 어떻게 이루어졌나요? ArchUnit으로 패키지 간 의존성 및 계층 구조 규칙을 정하고 테스트에 반영했으며, SonarCloud는 정적 분석을 통해 코드 스멜, 보안 이슈, 중복을 점검하고 PR 리뷰에 반영해 지속적으로 품질을 개선했습니다.
Q. ZGC
와 G1GC
를 사용한 경험이 모두 있으신데, 각각의 GC를 선택한 이유와 성능 차이는 어땠나요? G1GC는 예측 가능한 GC 시간을 제공하지만 힙 사이즈 증가에 따라 Stop-The-World 시간이 늘어났습니다. ZGC는 낮은 지연 시간과 STW 최소화를 통해 대용량 배치와 실시간 작업에서 더 안정적인 성능을 보였습니다. 단, 메모리 소비량은 ZGC가 더 높았습니다.
콘텐츠 플랫폼 전면 개편
Q. JSP 기반 레거시에서 어떤 문제들이 있었고, 그것을 어떤 구조로 개편했나요? 프론트에 집중된 비즈니스 로직, 비정형 Map 기반 DTO 사용, 검증 부재 등으로 유지보수와 타입 안정성이 저하됐습니다. 이를 API 명세 기반의 DTO 구조, 서비스 계층 도입, 유효성 검증 계층 구성으로 개편했습니다.
Q. DTO 중심의 정적 타입 구조로의 전환 시 유효성 검증 체계를 어떻게 구성했는지 설명해주세요. JSR-380](https://beanvalidation.org/2.0-jsr380/) 기반 Bean Validation을 활용하고, 커스텀 애노테이션을 통해 도메인 규칙 검증을 수행했습니다. Controller, DTO, Service 계층 각각에서 역할에 맞게 검증을 분리하여 책임을 명확히 했습니다.
Q. 계약 코드 생성 시 병목과 락 경합 문제를 어떻게 해결했는지 기술적으로 상세히 설명해주세요. 기존 시퀀스 테이블 기반 ID 생성 구조를 유지하되, 애플리케이션 레벨에서 사전 확보(Pre-Fetch)된 ID 캐시를 메모리에 저장하고 소비하는 구조로 변경했습니다. 충돌 방지를 위해 낙관적 락을 적용했습니다.
Q. 낙관적 락 적용과 메모리 캐싱 방식 도입 후의 성능 개선 수치는 어떻게 측정했나요? 배포 전후 트랜잭션 처리 시간, 응답 속도, 동시 요청 처리량을 기준으로 Datadog APM과 로그 기반 지표를 수집해 개선 수치를 측정했습니다.
글로벌 콘텐츠 유통 시스템 구축
Q. DDEX 기반 음원 전송 시스템은 어떤 방식으로 구현되었나요? 앨범/곡 메타데이터를 DDEX XML로 변환한 후 FTP/SFTP 또는 API를 통해 각 플랫폼으로 자동 전송하며, 전송 성공 여부를 비동기로 수신받는 구조입니다.
Q. RBAC 기반 접근 제어를 어떻게 설계하고 구현하셨나요? 저희는 Spring Security의 메서드 보안 기능을 기반으로 RBAC(Role-Based Access Control) 구조를 구성했습니다. 정적 역할(Role) 기반 제어는 @Secured
로 처리하고, 권한 granularity가 더 필요한 경우는 @PreAuthorize
를 사용하되, SpEL을 직접 사용하지 않고 SecurityContext 내 권한 정보만으로 판단하도록 구현했습니다.
Q. 메시지 큐(SQS)를 활용한 비동기 엑셀 다운로드 처리를 어떻게 구성했는지 설명해주세요. 엑셀 요청 시 조건을 메시지로 전송하여 별도의 워커 서버가 비동기 처리한 뒤, 결과를 S3에 업로드하고 사용자는 상태를 Polling하여 확인하도록 구성했습니다.
Q. 전체 다운로드 요청을 예측 기반으로 처리하신 방식의 핵심 로직은 어떤 기준으로 설계되었나요? 엑셀 다운로드 요청 중 일부는 항상 유사한 조건으로 반복되거나, 전체 데이터를 조회하는 패턴이 많아 API 서버 부하가 집중되는 경향이 있었습니다. 이를 해결하기 위해, 예측 기반 캐싱 전략을 도입했습니다. 조건 조합에 따라 사전 생성 대상으로 분기하거나 실시간 처리로 분기하는 로직을 구성했습니다.
대용량 배치 및 엑셀 처리 최적화
Q. Apache POI Streaming API를 선택한 이유는 무엇인가요? 대용량 엑셀 파일을 생성하는 기능에서, 기존의 XSSFWorkbook
기반 엑셀 생성 방식은 전체 데이터를 메모리에 로딩하여 처리하기 때문에 메모리 사용량이 급증하거나 OOM(OutOfMemoryError)이 발생하는 이슈가 있었습니다. 이를 해결하기 위해 Apache POI의 Streaming API (SXSSF) 를 채택했습니다.
Q. FastExcel 대신 Apache POI를 사용하신 이유가 있나요? ❌ FastExcel: 더 빠르고 가볍지만 기능 제한 (머지, 셀 스타일 등 미지원), 기존 시스템과 호환성 문제, 사용자 지정 포맷 요구사항 충족 어려움 ❌ EasyExcel (Alibaba): 내부적으로 JDK reflection 사용 → 추적 및 디버깅 어려움, 커스터마이징 난이도 ↑ ⭕️ SXSSF (POI Streaming) 선택 이유: 풍부한 커스터마이징 (스타일, 셀 병합 등) 지원, Spring Batch / POI 연동 경험이 있는 팀원들이 많음, 이미 사용 중인 POI 기반 코드와 호환 쉬움
Q. EntityManager.flush()/clear()
호출 주기 조절을 통해 얻은 효과는 무엇이었나요? 대용량 데이터를 저장(persist/merge)하는 배치 작업에서, JPA는 엔티티 객체를 내부적으로 1차 캐시(영속성 컨텍스트) 에 계속 보관하게 됩니다. 이로 인해 다음과 같은 문제가 발생할 수 있었습니다: 메모리 사용량 급증, Full GC 빈도 증가, OutOfMemoryError 발생, 속도 저하 및 처리 지연. 이 문제를 해결하기 위해 EntityManager.flush()
와 clear()
를 주기적으로 호출하는 방식으로 개선하였고, 이로 인해 Full GC빈도를 줄이고 메모리 사용량을 낮출 수 있었습니다.
Q. Full GC 문제를 Thread Dump, Heap Dump 분석을 통해 어떻게 파악하고 개선하셨나요? Heap Dump, GC 로그를 분석하여 특정 시점에 객체 수가 급증하는 것을 확인했고, 해당 배치에서 Flush 지연이 원인임을 파악하여 Chunk 크기 조정 및 반복 Clear를 적용했습니다.
대량 데이터 병렬 처리 및 락 경쟁 해소
Q. Redis 기반 분산 락을 어떻게 구현했으며, 어떤 동시성 이슈를 방지했나요? Redisson의 RLock
을 활용하여 배치 작업 시작 시 키 락을 선점하고, 작업 완료 후 해제하는 구조로 설계했습니다. TTL 설정으로 락 유실도 방지했습니다.
Q. 협회 지분율 데이터의 특성과 분기 처리 전략을 구체적으로 설명해주세요. 월 단위, 대량 일괄 등록이라는 특성상 배치 트리거 빈도가 낮아 분기 처리 전략이 유효했고, 트랜잭션 단위로 5만 건씩 나누어 순차 혹은 병렬 처리하도록 구성했습니다.
오디오 콘텐츠 제작 플랫폼 구축 및 인증 인프라 개선
Q. @TransactionalEventListener
를 활용한 외부 API 호출 처리 흐름을 설명해주세요. Entity 저장 후 발행되는 이벤트 리스너에서 외부 API 호출 로직을 수행하도록 구성함으로써, 커밋 이후에만 호출되도록 보장했습니다.
Q. Redis를 통한 인증 토큰 저장 전략의 TTL 관리 방식은 어떤 기준으로 설계했나요? 액세스 토큰의 exp
필드를 기준으로 TTL을 설정하되, 서버 간 시간 차이를 고려해 버퍼를 추가 적용했습니다.
Q. ISMS 대응을 위한 로그 수집 체계 개선 과정에서 가장 어려웠던 부분은 무엇이었나요? 각 기능에 대한 로깅 범위 정의와 실시간 로그 필터링 기준 설정이 가장 어려웠습니다. 개발자마다 로깅 기준이 달라 이를 통합하는 작업에 시간이 소요됐습니다.
Q. Filebeat, ELK 스택 기반 로그 분석 구조를 어떤 순서로 구축하셨는지 설명해주세요. JSON 포맷 로그 정의 → Logback MDC 설정 → Filebeat 수집기 설정 → Elasticsearch 저장 → Kibana 대시보드 구성
확장형 응용 질문
Q. 메시지 큐, 배치, 캐싱, 이벤트리스너를 적절히 조합하여 “대용량 데이터에 대해 정합성을 유지하며 빠르게 처리하는 시스템”을 설계하라고 하면 어떤 아키텍처를 제안하시겠습니까? Kafka 또는 SQS 기반 메시징으로 요청을 분산하고, Spring Batch 혹은 비동기 Worker로 처리 후, 처리 결과를 Redis/S3에 캐싱하여 API 레이어는 빠른 응답 제공. 트랜잭션 정합성은 DB 락 또는 분산 트랜잭션(Eventual Consistency) 모델로 유지.
Q. Package by Component 구조 하에서 팀 협업 시 발생할 수 있는 문제점과 해결 전략은 어떤 것이 있을까요? 명확한 디렉토리 기준과 각 컴포넌트별 책임 구분 문서를 작성하고, 공통 모듈의 의존성 분리 및 인터페이스 기반으로 팀 간 충돌 최소화.
Q. 만약 MDS 시스템에 실시간 정산 기능을 도입한다고 하면 어떤 기술과 구조를 제안하실 수 있나요? CDC 기반 Kafka 스트리밍으로 거래 데이터 실시간 수집 → Flink/Spark로 처리 → Redis/Elastic 등에 실시간 누적 및 조회 제공. 정합성 보장을 위해 수신 로그 및 비즈니스 이벤트 저장소 구성.
지인의 피드백
Q. ThreadDump, HeapDump 하실 때 사용하신 툴은 어떤 건가요?
ThreadDump:
- VisualVM, JDK Mission Control, 또는
jstack
명령어를 사용했습니다. - 운영 중인 서버에서는
jstack -l <pid>
명령어로 추출하고, VisualVM에서 시각적으로 분석했습니다.
HeapDump:
jmap -dump:format=b,file=heap.hprof <pid>
명령어를 사용하거나,-XX:+HeapDumpOnOutOfMemoryError
옵션으로 OOM 발생 시 자동 추출되도록 설정했습니다.- 분석 도구는 Eclipse MAT, VisualVM, 또는 JDK Flight Recorder를 사용했습니다.
Q. OOM이 발생할 때 HeapDump 하도록 하는 JVM 옵션을 주셨나요?
네, 다음과 같은 JVM 옵션을 설정했습니다:
1
2
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/var/log/heap.hprof
이 옵션을 설정하면 OOM 발생 시 자동으로 heap dump가 지정된 경로에 생성되어 추후 분석에 사용됩니다.
Q. JPA 대신 JPQL이나 nativeQuery를 사용하면 1차 캐시를 쓰지 않기 때문에 flush/clear 없이도 성능이 낫지 않을까요?
맞습니다. JPQL 또는 native query를 사용하는 경우 EntityManager
의 1차 캐시를 거치지 않으므로,
- 영속성 컨텍스트에 엔티티가 누적되지 않아 flush/clear가 필요 없는 경우가 많습니다.
- 특히 읽기 전용 대용량 조회(batch) 의 경우 native query나
@Query(nativeQuery = true)
로 수행하는 것이 훨씬 효율적입니다.
다만, 아래와 같은 조건에서는 여전히 JPA flush/clear 전략이 필요합니다:
- 엔티티를 수정하거나 저장(persist)하는 경우
- 트랜잭션 내에 수천 건 이상 처리하는 경우 (쓰기 작업 포함)
즉, 단순 조회라면 JPQL/nativeQuery를 사용하고 flush/clear 생략이 합리적입니다.
Q. 다른 세션에서 같은 row에 변경을 일으키면 Deadlock이 발생했을 것 같은데, service DB와 batch DB를 함께 쓴 상황인가요?
네, 맞습니다. 동일한 테이블을 서비스와 배치가 동시에 갱신하는 상황에서 다음 조건이 충족되면 Deadlock이 발생할 수 있습니다:
- 서로 다른 트랜잭션에서 동일 row에 update/lock을 시도
- InnoDB의 row-level locking 구조에 따라 lock 순서가 꼬일 경우
특히 다음 상황이 위험합니다:
- 배치에서 다량의 데이터를
DELETE → INSERT
패턴으로 갱신 - 서비스에서는 동일 row를
UPDATE
하거나 조회 시FOR UPDATE
를 수행
해결을 위해 다음을 적용했습니다:
- 배치 실행 시 Redis 락을 통한 선점 처리
- DB 레벨에서 분할 처리하거나 작업 시간대 조정
SELECT ... FOR UPDATE SKIP LOCKED
전략 고려
Q. TransactionalEventListener는 활성화된 트랜잭션이 없으면 작동하지 않는데, 이런 케이스는 고려하셨나요?
네, @TransactionalEventListener
는 AFTER_COMMIT
시점의 트랜잭션 후크를 활용하므로, 트랜잭션이 없는 경우 이벤트가 동작하지 않습니다.
이를 고려해 아래와 같은 전략을 적용했습니다:
- 트랜잭션이 존재하지 않을 수 있는 이벤트는
ApplicationEventPublisher
대신 별도의 서비스에서 직접 처리하거나@EventListener
로 fallback 처리 - 이벤트 발행 전
TransactionSynchronizationManager.isActualTransactionActive()
를 검사하여 트랜잭션 유무를 판단 - 테스트 환경에서는
@Transactional(propagation = REQUIRES_NEW)
를 강제로 부여하여 이벤트 리스너 활성화 여부를 검증
즉, “트랜잭션이 없을 경우 이벤트가 동작하지 않는다”는 특성을 정확히 인지하고, 설계 시 필터링 로직 또는 대체 전략을 마련했습니다. @TransactionalEventListener
: 트랜잭션 유무에 따른 동작 원리와 실전 대응 전략