시니어 백엔드 면접 질문 3편 - 설계/리더십 (5~10년차)
시니어 개발자는 코드만 잘 짜는 게 아닙니다. 설계 의사결정, 팀 리딩, 기술 방향 제시까지 할 수 있어야 합니다.
시리즈 구성:
- 1편: 인프라/스케일링 - 트래픽, DB, 분산시스템
- 2편: 운영/안정성 - 장애대응, 이벤트 아키텍처, 배포
- 3편 (현재): 설계/리더십 - 의사결정, 시스템디자인, 코드리뷰
1. 설계 의사결정
Q1. 새로운 기술을 도입할 때 어떤 기준으로 판단하시나요?
기대 답변:
- 문제 해결: 현재 기술로 해결 안 되는 명확한 문제가 있는가?
- 팀 역량: 학습 곡선과 유지보수 가능성
- 생태계 성숙도: 커뮤니티, 문서, 안정성
- 운영 비용: 인프라 비용, 모니터링 도구 지원
💡 실제 사례 보기
시나리오: Java 8 기반 백엔드 50개 모듈. Kotlin 전환 제안이 올라옴.
의사결정 프로세스 (ADR로 문서화):
Context (배경):
- Java 8 → 17 업그레이드 예정이지만 NullPointerException 빈발
- 신규 입사자 중 Kotlin 경험자 비중 증가
옵션 비교:
| 옵션 | 장점 | 단점 |
|---|---|---|
| 전면 전환 | 빠른 일관성 | 50개 모듈 작업, 위험 |
| 신규만 Kotlin | 위험 적음 | 두 언어 혼재로 복잡 |
| 도입 안 함 | 변경 비용 0 | 향후 채용·생산성 손해 |
Decision: 신규 모듈만 Kotlin, 기존은 점진적 마이그레이션 (분기당 2~3개 모듈)
Consequences (예상):
- 6개월간 양 언어 혼재
- CI/CD 빌드 시간 +10% (예상)
- 학습 비용: 기존 인원 1인당 약 20시간
실제 결과 (1년 후 회고):
- 신규 코드의 NPE 90% 감소
- 코틀린 코드의 가독성 향상 (data class, null safety)
- 예상 못 한 비용: 라이브러리 호환성 일부 이슈 (특히 어노테이션 프로세서)
교훈:
- ADR이 왜 이 결정을 했는지 1년 뒤 새로 합류한 동료에게도 명확
- 기술 도입은 완벽한 결정이 아니라 명시적 합의된 트레이드오프
- “지금 안 하면 1년 뒤 더 큰 비용”이라는 시점 판단도 의사결정의 일부
🔄 꼬리질문 1: 기술 부채(Technical Debt)는 어떻게 관리하시나요?
기대 답변:
- 스프린트마다 일정 비율 할당 (예: 20%)
- 영향도와 긴급도 매트릭스로 우선순위
- 리팩토링 전 테스트 커버리지 확보
- 큰 부채는 별도 프로젝트로 계획
📋 실제 사례
상황: “기술 부채를 갚자”는 공감대는 있었지만 항상 기능 개발에 밀려 실제로는 한 줄도 안 갚히는 상태가 1년 넘게 지속. 코드베이스가 점점 손대기 무서운 영역이 늘어남.
진단:
- 부채가 어디에 얼마나 있는지 가시화되지 않아 우선순위 논의 자체가 불가
- “급한 기능”은 항상 있어서 부채는 영원히 후순위
조치 — 부채 가시화 + 정량 할당:
- Notion 보드에 기술 부채를 영향도 × 긴급도 매트릭스로 등록 (각 항목에 예상 공수)
- 매 스프린트 capacity의 20%를 부채에 고정 할당 (협상 불가 규칙으로 못 박음)
- 분기마다 상위 5개를 선정해 처리
결과:
- 1년 후 critical 등급 부채 0개
- “손대기 무서운 영역”이 줄어 기능 개발 속도도 오히려 빨라짐
교훈: 기술 부채는 보이지 않으면 영원히 안 갚힌다. 매트릭스로 가시화 + capacity의 고정 비율 할당(협상 불가) 이 핵심. “여유 있을 때 하자”는 영원히 안 온다.
🔄 꼬리질문 2: 아키텍처 의사결정 기록(ADR)을 작성해보신 적 있나요?
기대 답변:
- Context: 배경과 제약사항
- Decision: 선택한 방안
- Consequences: 예상되는 장단점
- 팀 내 공유와 온보딩에 활용. 나중에 “왜 이렇게 했지?” 방지.
📋 실제 사례
상황: 입사 1년 차에 “이 모듈은 왜 이렇게 설계됐지?” 라는 질문에 아무도 명확히 답하지 못하는 경우가 잦았음. 원 설계자는 이미 퇴사. 결국 추측으로 리팩토링했다가 숨은 이유를 건드려 장애 유발.
조치 — ADR 문화 도입:
docs/adr/디렉토리에 마크다운으로 모든 중요 결정을 기록 (Context / Decision / Consequences)- 결정을 번복할 땐 기존 ADR을 삭제 않고
Superseded by ADR-031로 표시 → 의사결정 이력 보존 - 신규 입사자 온보딩에 ADR 디렉토리를 필수 읽기 자료로
결과:
- 1년간 ADR 30건 누적
- 신규 입사자가 “왜 Kotlin인지·왜 이 패턴인지” 를 별도 설명 없이 파악
- “숨은 이유를 모르고 건드리는” 장애 소멸
교훈: ADR은 과거의 나와 미래의 동료에게 보내는 편지다. “왜”가 기록되지 않으면 다음 사람이 추측으로 건드려 사고를 낸다. 번복할 때도 이력을 지우지 말고 Superseded로 남긴다.
🔄 꼬리질문 3: 기술 선택에서 실패한 경험이 있다면?
이 질문의 의도: 완벽한 결정만 있는 건 아닙니다. 실패 경험에서 무엇을 배웠는지, 어떻게 수정했는지가 더 중요합니다.
📋 실제 사례
상황: “감사 로그가 필요하다”는 이유로 주문 도메인 전체에 Event Sourcing을 도입. 도입 당시엔 멋진 결정 같았으나, 6개월 운영하며 비용이 예상의 5배로 드러남.
무엇이 실패했나:
- 프로젝션(이벤트 → 현재 상태) 디버깅이 일반 CRUD보다 훨씬 어려움
- 비즈니스 규칙이 바뀔 때마다 과거 이벤트 리플레이·재계산 비용 폭발
- 정작 감사 로그는 별도 audit 테이블로도 충분했음 (ES까지 필요 없었음)
조치 — 부분 회귀:
- 감사가 법적으로 필수인 결제·정산 도메인만 ES 유지
- 나머지 주문 하위 도메인은 CRUD + audit 테이블로 회귀
- 회고에서 “왜 과도했나”를 ADR로 기록 (다음 사람이 같은 실수 반복 방지)
결과:
- 운영 복잡도 대폭 감소, 개발 속도 회복
- “감사 = ES”가 아니라 “감사 요구의 강도에 맞는 도구” 라는 기준 정립
교훈: 실패는 결정 자체의 잘못보다 trade-off 측정 부족이 더 흔한 원인이다. “멋진 패턴”에 끌려 필요 이상으로 도입하면 운영이 무너진다. 실패 후 부분 회귀를 부끄러워하지 않는 것도 시니어 역량.
2. 시스템 디자인
Q2. URL 단축 서비스를 설계해주세요.
기대 답변:
- 요구사항 확인: 일일 요청 수, 저장 기간, 읽기/쓰기 비율
- 단축 알고리즘: Base62 인코딩, Hash + 충돌 처리
- 저장소: 키-값 구조, Redis(캐시) + DB(영구)
- 확장성: 샤딩(short key prefix 기준), Read Replica
💡 실제 사례 보기
시나리오: 일일 생성 1,000만 / 일일 리다이렉트 10억, 5년 보관 요구.
용량 산정:
- 5년 누적 단축 URL: 약 180억 건
- 평균 row 크기 200B → DB 3.6TB
- 리다이렉트 QPS: 10억 / 86400s ≈ 12,000 QPS (평균), 피크 40,000 QPS
핵심 결정:
1) Short Key 생성 — Snowflake → Base62
- Hash 충돌 처리는 코드가 복잡 → Snowflake 64bit unique ID → Base62로 인코딩 (11자)
- 충돌 없음, 분산 환경 친화
2) 저장 — Cassandra
- 180억 건 + write-heavy + 단일 키 조회 → Cassandra가 적합
- 파티션 키 = short_key, replica factor 3
3) Read Path — 3단 캐시
- L1: CDN (Cloudflare 301 응답, 1주 TTL) → 핫 URL은 origin 안 옴
- L2: Redis 클러스터 (TTL 1시간)
- L3: Cassandra
- 측정 결과 CDN hit 70%, Redis hit 25%, Cassandra hit 5%
4) 보안
- 생성 시 Google Safe Browsing API 비동기 검사
- 위험 판정되면 short_key TTL 즉시 0으로 (Redis), Cassandra는 status=BLOCKED
- 동일 사용자 분당 100건 이상 생성 시 rate limit
병목 / 트레이드오프:
- Cassandra는 읽기 쿼리 유연성이 약함 → 통계는 별도 Kafka → ClickHouse
- 핫 URL 분포가 80/20보다 더 치우침 (top 0.1%가 트래픽의 60%) → CDN 캐시 의존도가 가장 큼
교훈:
- 시스템 디자인 면접은 완벽한 답이 아니라 요구사항 → 트레이드오프 → 결정의 흐름
- 숫자(QPS, 용량)를 먼저 산정해야 왜 이 기술인지 논리가 섬
🔄 꼬리질문 1: Short URL 충돌은 어떻게 처리하나요?
기대 답변:
- Hash 충돌: 다른 suffix 추가 후 재시도
- Unique ID 기반: Snowflake, UUID 사용
- DB Unique Constraint: 삽입 실패 시 재생성
📋 실제 사례
상황: 초기 설계는 원본 URL의 MD5 해시 앞 7자를 short key로 사용. 1억 건을 넘어가자 생일 역설로 충돌이 0.3% 발생, 충돌 시 재시도 로직이 DB를 추가로 때려 쓰기 성능 저하.
진단:
- 7자 Base62 공간(62^7 ≈ 3.5조)이 충분해 보였지만, 생일 역설상 1억 건이면 충돌 확률이 무시 못 할 수준
- 충돌 → 재시도 → 다시 충돌 체크의 DB 왕복이 쓰기 경로를 느리게 함
조치 — Snowflake + Base62로 전환:
- 해시 대신 Snowflake 64bit unique ID 생성 (타임스탬프 + 워커 ID + 시퀀스)
- Base62 인코딩 → 11자 short key
- 구조적으로 충돌 불가능 (분산 환경에서도 유일성 보장)
결과:
- 충돌 0건, 충돌 재시도 DB 왕복 제거로 쓰기 latency 개선
- 워커 ID로 분산 생성도 안전
교훈: 해시 기반 short key는 생일 역설로 규모가 커지면 결국 충돌한다. Snowflake 같은 unique ID 생성기가 충돌 처리 로직 자체를 없애는 정석이다. “충돌나면 재시도”는 규모에서 성능 부채가 된다.
🔄 꼬리질문 2: 초당 10만 요청이 들어온다면?
기대 답변:
- 캐싱: 인기 URL은 Redis에 캐시
- Read Replica: 읽기 분산
- CDN: 리다이렉트 응답 캐싱
- 로드밸런서: 요청 분산
📋 실제 사례
상황: 마케팅 캠페인 단축 URL 하나가 초당 10만 리다이렉트를 받기 시작. 처음엔 모든 리다이렉트가 Redis → Cassandra로 내려가 origin 부하 폭증, 응답 지연.
진단:
- 리다이렉트 응답(301 Location 헤더)은 사실상 불변 (한 번 만들어지면 안 바뀜)
- 그런데 매번 origin까지 조회 → 캐시 가능한 응답을 안 캐시하고 있었음
- 트래픽 분포: top 0.1% URL이 전체의 60% (멱법칙)
조치 — 3단 캐시 + CDN 301:
- CDN(Cloudflare) edge에서 301 응답 자체를 캐시 (1주 TTL) → 핫 URL은 origin 도달 0
- L2 Redis 클러스터 (TTL 1시간), L3 Cassandra
- 측정: CDN hit 70% / Redis hit 25% / origin 5%
결과:
- origin 도달 QPS: 100k → 5k (95% 감소)
- 핫 URL은 사용자와 가장 가까운 CDN edge에서 즉시 응답
교훈: 리다이렉트처럼 불변에 가까운 응답은 CDN edge 캐시가 가장 큰 레버리지다. Read Replica·Redis 이전에 “이 응답이 캐시 가능한가?” 를 먼저 묻는다. 멱법칙 트래픽일수록 edge 캐시 효과가 극대화된다.
🔄 꼬리질문 3: 악성 URL 차단은 어떻게 하시겠어요?
기대 답변:
- 생성 시 검사: Google Safe Browsing API
- 신고 기능: 사용자 리포트
- 주기적 스캔: 비동기 배치로 검사
- Rate Limiting: 대량 생성 방지
📋 실제 사례
상황: 피싱 조직이 단축 URL 서비스를 악용 — 한 IP에서 분당 5,000건씩 피싱 사이트 단축 URL을 대량 생성해 스팸 문자로 유포. 서비스 도메인 자체가 피싱 블랙리스트에 오를 위기.
조치 — 다층 방어:
- Rate Limiting: IP·계정당 분당 생성 100건 제한 → 대량 생성 차단
- 생성 시 검사: Google Safe Browsing API로 비동기 검사 (생성 속도는 유지)
- 위험 판정 시: short key를 즉시 무효화 (Redis TTL 0 + Cassandra status=BLOCKED)
- 사후 스캔: 이미 생성된 URL도 주기적 배치로 재검사 (나중에 악성으로 바뀐 도메인 대응)
- 신고 기능: 사용자 리포트 → 자동 분석 파이프라인
결과:
- 피싱 대량 생성 차단, 도메인 블랙리스트 등재 회피
- 정상 사용자 생성 속도는 영향 없음 (검사가 비동기)
교훈: 보안은 단일 메커니즘으로는 반드시 우회된다. Rate Limit(양) + Safe Browsing(생성 시) + 사후 스캔(시간차) + 신고(사용자) 의 다층 방어가 필요하다. 특히 검사는 비동기로 해야 정상 사용자 UX를 안 해친다.
3. 코드 리뷰와 품질
Q3. 코드 리뷰할 때 어떤 점을 중점적으로 보시나요?
기대 답변:
- 의도 파악: 왜 이렇게 작성했는지 이해
- 버그 가능성: 경계 조건, null 처리, 동시성
- 가독성: 네이밍, 구조, 복잡도
- 테스트: 충분한 커버리지, 엣지 케이스
- 설계: 책임 분리, 확장성
💡 실제 사례 보기
시나리오: 결제 환불 PR 리뷰 — 코드는 깔끔하고 테스트도 통과. 그러나 비즈니스 시나리오 검증으로 critical 결함 발견.
PR 요약:
fun refund(orderId: Long, amount: BigDecimal): RefundResult {
val order = orderRepo.findById(orderId)
pgClient.refund(order.pgTxId, amount)
order.refundedAmount += amount
orderRepo.save(order)
return RefundResult.success()
}리뷰어가 짚은 것들:
-
🚨 부분 환불 누적 검증 없음
refundedAmount + amount > order.totalAmount인 경우 → 환불 금액이 결제액 초과- PG사가 실패 응답을 줘도 우리 DB에는 가산되는 경로 가능
- 코멘트: “이미 환불된 금액 합산 후 검증 부탁드려요. 테스트 케이스도 추가 부탁드립니다.”
-
🚨 트랜잭션 경계 문제
- PG 호출 → DB 저장 사이에 서버 다운 시 PG는 환불됐는데 우리 DB는 모름 (돈만 새어 나감)
- 제안: Outbox 패턴 또는 PG 응답 idempotency key로 재조회
-
⚠️ 로그/감사 부재
- 환불은 회계 감사 대상 → 누가·언제·얼마를 이전 후 상태까지 기록 필요
-
✅ 좋았던 점 (먼저 칭찬)
- 메서드 시그니처가 직관적
- 의존성 주입 패턴 일관
결과:
- 부분 환불 누적 검증 + Outbox 도입으로 재작업
- 머지 후 1개월간 부분 환불 시 음수 잔액 사고 없음
- 작성자가 다음 PR부터 비슷한 패턴을 스스로 검토
교훈:
- 코드가 깔끔해도 비즈니스 시나리오 검증이 코드 리뷰의 핵심
- 도메인 지식이 없는 리뷰는 “스타일 리뷰”에 그침
- 칭찬 → 핵심 2~3개 지적 → 대안 제시 순서가 작성자 입장에서 받아들이기 쉬움
🔄 꼬리질문 1: 리뷰에서 의견 충돌이 있으면 어떻게 하시나요?
기대 답변:
- 먼저 상대 의견을 충분히 이해
- 객관적 근거(성능, 가독성, 유지보수)로 토론
- 합의 안 되면 팀 컨벤션 또는 테크 리드 판단 요청
- “나중에 개선하자”로 타협하기보다 지금 결정
📋 실제 사례
상황: 같은 종류의 논쟁(예: “의존성 주입 vs Service Locator”, “DTO 매핑을 어디서 할 것인가”)이 PR마다 반복되며 매번 30분씩 댓글 토론. 결론도 PR마다 달라 코드베이스 일관성이 깨짐.
진단:
- 매번 같은 논쟁을 처음부터 → 시간 낭비 + 감정 소모
- 개인 선호가 부딪혀 결론이 PR마다 다름 → 일관성 부재
조치 — 반복 논쟁의 ADR화:
- 자주 부딪히는 주제는 양쪽 의견·장단점을 정리한 ADR 작성 후 팀 합의로 컨벤션 확정
- 이후 같은 논쟁이 PR에 나오면 “ADR-014 참고” 한 줄로 종결
- 컨벤션에 없는 새 주제만 토론
결과:
- 반복 논쟁 0회 (ADR로 한 번에 종결)
- 코드베이스 일관성 회복, 리뷰 시간 단축
교훈: 리뷰에서 반복되는 의견 충돌은 개인 설득이 아니라 문서(ADR/컨벤션)로 종결해야 한다. 같은 논쟁을 매번 처음부터 하면 팀 시간이 녹는다. 결론을 한 곳에 박아두는 것이 시니어의 역할.
🔄 꼬리질문 2: 주니어 코드를 리뷰할 때 특별히 신경 쓰는 점은?
기대 답변:
- 교육적 피드백: “이건 틀렸어” 대신 “이렇게 하면 더 나아요 + 이유”
- 칭찬 먼저: 좋은 점 언급 후 개선점 제시
- 질문 형태: “이건 왜 이렇게 했어요?”로 생각 유도
- 너무 많은 지적 피하기: 핵심 2-3개에 집중
📋 실제 사례
상황: 주니어 PR에 개선점 20개를 빠짐없이 코멘트로 달았더니, 작성자가 의기소침해져 다음부터 PR 올리기를 두려워하고 작업 속도가 오히려 떨어짐.
진단:
- 20개 코멘트는 맞는 지적이었지만, 받는 사람 입장에선 “내 코드가 다 틀렸다”는 메시지
- 우선순위 없이 나열되어 뭐가 중요한지 구분 안 됨
조치 — 리뷰 방식 전환:
- 핵심 2~3개만 지적하고 나머지 사소한 건 “nit:” 접두사로 (반영 선택)
- 잘한 점 1개를 먼저 명시적으로 칭찬
- 지적도 “왜 이렇게 하셨어요?” 질문 형태로 → 본인이 생각하게 유도
- 복잡한 건 댓글 대신 페어 리뷰(같이 보면서 설명)
결과:
- 3개월 후 주니어 PR 품질이 스스로 향상 (핵심 패턴을 체득)
- PR 올리는 적극성·작업 속도 회복
교훈: 주니어 리뷰는 교정이 아니라 대화다. 20개 맞는 지적보다 핵심 2~3개 + 칭찬 1개 + 질문형 피드백이 사람을 성장시킨다. 받는 사람이 위축되면 리뷰는 실패한 것.
🔄 꼬리질문 3: 코드 리뷰 없이 머지된 코드에서 버그가 발견되면?
기대 답변:
- 먼저 버그 수정 (blame 아닌 해결 우선)
- 프로세스 점검: 왜 리뷰 없이 머지됐는지
- 자동화: CI에 필수 리뷰어, 테스트 통과 조건 추가
- 팀 회고: 재발 방지 논의
📋 실제 사례
상황: “긴급 핫픽스”라는 명목으로 리뷰 없이 바로 머지가 관행화되어 있었음. 그러다 한 핫픽스가 정합성 버그를 동반해 운영에 반영, 2차 장애 유발.
진단:
- “긴급”이라는 예외 프로세스가 일상이 되어 있었음
- 리뷰가 시스템이 아니라 개인 선의에 의존 (바쁘면 스킵)
조치 — 예외를 시스템으로:
- GitHub branch protection: 필수 리뷰어 1명 + CI 통과 없으면 머지 불가 (긴급도 예외 없음)
- 진짜 긴급 시: 사후 리뷰 의무화 (머지 후 24시간 내 리뷰 + 회고)
- 핫픽스 전용 체크리스트 (롤백 계획·테스트 범위 명시)
결과:
- 리뷰 없는 머지 0건
- 핫픽스도 최소 안전망을 거치게 되어 2차 장애 소멸
교훈: 예외 프로세스(긴급 머지)가 일상이 되면 그건 시스템 부재다. 버그 발견 시 blame 대신 “왜 이게 가능했나”를 묻고, 리뷰를 개인 선의가 아니라 CI 강제로 만든다. 긴급도 사후 리뷰로 안전망을 둔다.
4. 팀 리딩과 멘토링
Q4. 주니어 개발자 멘토링은 어떻게 하시나요?
기대 답변:
- 1:1 미팅: 주 1회, 업무 외 고민도 공유
- 페어 프로그래밍: 복잡한 작업은 함께
- 점진적 위임: 작은 기능 → 큰 기능 → 설계 참여
- 심리적 안전감: 질문해도 괜찮다는 분위기
💡 실제 사례 보기
시나리오: 신입 6개월 차 주니어를 결제 모듈 sub-owner로 키운 8개월 여정.
0~2개월: 안전한 작업으로 신뢰 쌓기
- 버그 수정 PR 중심 (작은 범위, 명확한 정답)
- 매 PR에 왜 이렇게 고쳤는지 1줄 코멘트 강요 → 의사결정 근거 훈련
- 1:1에서 기술 외 고민도 가볍게 (적응, 동료 관계)
2~4개월: 한 기능 단위 책임
- 환불 정책 변경 기능을 설계부터 맡김
- 페어 디자인 세션 (시니어가 질문만): “왜 동기로 처리하나요?”, “실패하면 어떻게 되나요?”
- 처음 작성한 설계서는 50% 수정 → 본인이 직접 보강
4~6개월: 코드 리뷰어 역할
- 다른 동료 PR에 리뷰어로 지정
- 처음엔 시니어가 같이 리뷰 → “이런 시각은 어떨까요?” 가이드
- 점차 독립적으로 비즈니스 시나리오까지 짚는 리뷰 작성
6~8개월: Sub-owner
- 결제 모듈 PR 절반은 본인이 마지막 승인자
- 모듈 ADR 1건 작성 (refund 보상 트랜잭션 도입)
- 장애 대응 후 포스트모템 호스트 역할
8개월 후 결과:
- 시니어 의존 없이 결제 모듈 운영 가능
- 본인 동기부여 강해짐 (모듈 오너십 = 성장 자산)
- 시니어는 다른 어려운 문제에 시간을 쓸 수 있게 됨
교훈:
- 멘토링은 지식 전달이 아니라 점진적 책임 위임
- 처음엔 답을 주지 말고 질문하기가 가장 효과적
- 1:1은 업무 외 고민까지 들어줘야 진짜 신뢰 형성
🔄 꼬리질문 1: 팀원이 계속 같은 실수를 반복하면?
기대 답변:
- 먼저 근본 원인 파악 (지식 부족? 집중력? 프로세스 문제?)
- 체크리스트나 자동화로 실수 방지
- 1:1에서 솔직하게 피드백
- 개선 계획 함께 수립
📋 실제 사례
상황: 한 팀원이 트랜잭션 경계 실수(외부 API 호출을 @Transactional 안에 넣어 커넥션 점유)를 PR마다 반복. 매번 리뷰에서 지적해도 다음 PR에서 또 발생.
진단 — 1:1에서 근본 원인 청취:
- 본인은 왜 문제인지 개념적으로 이해 못 함 (“동작은 하는데 왜 안 되죠?“)
- 즉, 의지·집중력 문제가 아니라 지식 갭 + 리뷰 때만 알고 작성 시엔 못 떠올림
조치 — 교육 + 자동화 병행:
- 트랜잭션·커넥션 풀 동작을 페어로 함께 디버깅하며 체득시킴 (왜 위험한지 눈으로)
- PR 템플릿에 “@Transactional 안에 외부 호출 없는지 확인” 체크리스트 항목 추가
- ArchUnit 규칙으로 트랜잭션 메서드 내 HTTP 클라이언트 호출 자동 탐지 → CI 경고
결과:
- 같은 실수 0건 (이해 + 자동 안전망)
- 본인이 왜 문제인지 설명할 수 있게 됨
교훈: 반복 실수는 개인 탓보다 지식 갭 또는 시스템 부재의 신호다. 1:1로 근본 원인(지식? 집중력? 프로세스?)을 먼저 파악하고, 교육(이해) + 자동화(안전망) 를 병행한다. 자동 탐지가 인간 주의력보다 신뢰할 수 있다.
🔄 꼬리질문 2: 팀 내 기술 공유는 어떻게 하시나요?
기대 답변:
- 주간 기술 공유: 15-30분 발표
- Wiki/Notion 문서화: 팀 지식 축적
- 스터디 그룹: 관심 주제별 소그룹
- 장애 리뷰 공유: 실수에서 배우기
📋 실제 사례
상황: “기술 공유 세션을 하자”고 했지만 슬라이드 준비 부담 때문에 발표자가 안 나서 흐지부지. 지식이 개인 머릿속에만 쌓이고 온보딩 때마다 같은 설명 반복.
조치 — 부담 최소화 라이트닝 토크:
- 주 1회 15분 라이트닝 토크 (슬라이드 금지, 화이트보드 + 라이브 코딩만)
- 주제는 “이번 주 내가 배운 것 / 삽질한 것” 처럼 가벼운 것 OK
- 발표 후 3줄 요약만 Notion에 기록 → 검색 가능한 자산화
- 장애 회고도 이 자리에서 공유 (실수에서 배우기)
결과:
- 1년 누적 50건 공유 (부담이 낮아 지속됨)
- 신규 입사자 온보딩이 “Notion 검색 + 토크 아카이브” 로 절반 단축
- “이거 누가 알아요?”가 줄어듦
교훈: 기술 공유는 완벽한 발표를 요구하면 죽는다. 부담을 극단적으로 낮춘 작은 습관(15분·슬라이드 없이) 이 지속 가능성을 만든다. 핵심은 발표의 질이 아니라 지식이 개인 머릿속을 떠나 검색 가능해지는 것.
🔄 꼬리질문 3: 본인이 가장 성장했던 경험은?
이 질문의 의도: 성장 마인드셋이 있는지, 자신의 성장을 돌아볼 줄 아는지 확인합니다. 겸손함과 자기 인식 능력을 봅니다.
📋 실제 사례
상황: 3년 차에 처음으로 P1 결제 장애를 단독 리드해야 했던 새벽. 그전까지는 “코드만 잘 짜면 된다”고 생각했는데, 장애 한복판에서 기술 외의 것들이 더 중요함을 체감.
그 경험에서 배운 것:
- mitigation 우선: 근본 원인을 찾기 전에 일단 롤백으로 사용자 피해부터 멈추는 판단
- 커뮤니케이션: 워룸에서 상황을 비개발 이해관계자에게 실시간 공유하는 능력
- 비즈니스 영향 환산: “에러율 80%“가 아니라 “분당 매출 X원 손실” 로 말해야 우선순위가 정렬됨
- 사후 포스트모템 호스트로서 blame-free 분위기를 만드는 역할
변화:
- 이후 코드를 볼 때 “이게 장애 나면 어떻게 대응하지?” 운영 관점이 항상 따라붙음
- 설계 단계에서 관측성·롤백 가능성을 먼저 고려하게 됨
교훈: 가장 큰 성장은 편안한 학습이 아니라 불편한 상황에서의 책임이 만든다. 단독 장애 대응은 코드 시야를 운영·비즈니스·커뮤니케이션까지 확장시켰다. 성장 질문엔 “무엇을 배웠고 그 후 어떻게 달라졌는지” 를 구체 사례로 답한다.
5. 협업과 커뮤니케이션
Q5. 기획/디자인 팀과 의견 충돌이 있을 때 어떻게 하시나요?
기대 답변:
- 경청: 상대 의도와 배경 이해
- 데이터 기반: 기술적 제약, 비용, 일정을 구체적으로 설명
- 대안 제시: “안 돼요” 대신 “이렇게 하면 가능해요”
- 문서화: 결정 사항과 이유 기록
💡 실제 사례 보기
시나리오: PM이 2주 후 마케팅 이벤트에 신규 결제 수단(특정 페이) 통합을 요청. 개발 견적 6주.
1차 회의 — 충돌:
- PM: “마케팅 예산 5억 집행, 무조건 2주 후 가능해야 함”
- 개발: “기존 결제 추상화 부재, 회계 정산 연동까지 포함 6주”
- 분위기: 갈등 모드
전환점 — Trade-off 명시화:
개발이 만든 옵션 매트릭스를 들고 PM과 다시 회의:
| 옵션 | 기간 | 리스크 | 추후 비용 |
|---|---|---|---|
| A. 풀 통합 | 6주 | 낮음 | 0 |
| B. 결제만 통합, 정산은 수동 | 2주 | 중간 (운영 매일 30분) | 정산 자동화 1.5주 후 |
| C. 결제도 제휴사 API 직호출(추상화 우회) | 1주 | 높음 (다음 결제수단 추가 시 또 작업) | 기술 부채 ~3주 |
| D. 이벤트 연기 | 0 | 0 | 마케팅 예산 손실 |
결정 (PM과 합의):
- B 선택. 2주 안에 결제 통합, 정산은 운영팀이 매일 수동 (이벤트 4주간만)
- 이벤트 끝난 후 정산 자동화 작업 즉시 진행
결과:
- 이벤트 정상 진행, 매출 18억
- 운영팀과 사전 합의해 수동 정산 부담 분배
- 이벤트 종료 후 정산 자동화 완료 → 기술 부채 청산
교훈:
- “안 돼요”는 협업의 끝, “이렇게 하면 가능해요”는 협업의 시작
- 옵션 매트릭스가 감정 대화를 데이터 의사결정으로 전환
- 임시 우회를 선택했다면 청산 시점을 미리 합의해야 부채가 아닌 의도된 트레이드오프
🔄 꼬리질문 1: 일정이 부족한데 기능 추가 요청이 들어오면?
기대 답변:
- 현재 상황 투명하게 공유 (남은 일정, 리스크)
- Trade-off 제시: “이걸 하려면 저건 다음 스프린트로”
- 우선순위 재조정 요청
- 무리한 약속 안 하기 (야근으로 해결 X)
📋 실제 사례
상황: 스프린트 중반에 기획팀이 “이것도 이번에 같이 넣어달라” 는 추가 요청. “안 된다”고 하면 비협조적으로 보이고, “된다”고 하면 야근 + 품질 저하가 뻔한 상황.
조치 — “안 됨/됨”이 아니라 데이터로 협상:
- 현재 스프린트 백로그 + 신규 요청을 한 표에 놓고, 각 항목 공수 명시
- “신규 요청을 넣으면 기존 A 또는 B가 다음 스프린트로 밀린다“는 trade-off를 시각화
- 우선순위 결정 권한은 PM에게 넘김 (개발은 옵션·비용만 제공)
결과:
- PM이 직접 기존 항목 일부를 deprioritize → 신규 요청 수용
- 야근 없이 범위 조정으로 해결
- “개발이 무조건 막는다”가 아니라 “같이 우선순위를 정한다” 관계로 전환
교훈: 일정 협상은 “된다/안 된다”의 의지 싸움이 아니라 trade-off를 데이터로 보여주고 우선순위 결정을 위임하는 것이다. 야근으로 막으면 그게 기본값이 되어 다음에도 같은 요청이 온다.
🔄 꼬리질문 2: 원격 근무에서 협업 잘 하는 팁은?
기대 답변:
- 비동기 소통: 문서화 철저히, 컨텍스트 충분히 공유
- 오버 커뮤니케이션: 진행 상황 수시로 공유
- 화상 미팅: 복잡한 논의는 텍스트보다 얼굴 보고
- 명확한 기대치: 응답 시간, 회의 시간대 합의
📋 실제 사례
상황: 원격 100% 전환 직후, 모든 논의를 실시간 화상 회의로 처리하려다 보니 시차·집중 시간 충돌로 회의가 하루 5시간씩 차지, 정작 개발 시간이 사라짐.
조치 — async-first 원칙:
- 의사결정은 기본 비동기: Notion에 안건 + 충분한 컨텍스트를 미리 적고, 댓글·이모지 voting으로 결정
- 동기 회의는 비동기로 해결 안 되는 복잡한 논의에만 (화이트보드 필요한 설계 등)
- 진행 상황은 과하다 싶게 채널에 공유 (오버 커뮤니케이션)
- 응답 기대 시간·코어 타임을 팀 합의로 명문화
결과:
- 동기 회의 시간 50% 감소
- 시차 있는 팀원도 자기 시간에 의사결정 참여 가능
- 개발 집중 시간 회복
교훈: 원격의 핵심은 “언제 만나느냐” 가 아니라 “어떻게 비동기로 흐르느냐” 다. 모든 걸 실시간 회의로 하면 시차·집중 시간이 깨진다. 의사결정을 문서 + 비동기 voting으로 옮기고, 동기는 꼭 필요한 것만.
🔄 꼬리질문 3: 기술 결정을 비개발자에게 설명할 때 어떻게 하시나요?
기대 답변:
- 비유 활용: 기술 용어 대신 일상적 비유
- 영향 중심: “이게 뭔지”보다 “이게 왜 중요한지”
- 시각 자료: 다이어그램, 간단한 도식
- 질문 유도: 이해했는지 확인
📋 실제 사례
상황: DB 샤딩 작업으로 기능 개발이 2주 지연된다고 PM에게 설명해야 했음. “샤딩”, “수평 분할” 같은 용어로 설명하니 PM은 “왜 그게 2주나 걸리고 왜 지금 필요한지” 이해 못 하고 일정만 불만.
조치 — 비유 + 영향 중심:
- “DB를 책장이라고 하면, 지금 한 책장에 책이 꽉 차서 새 책을 못 꽂는 상태예요. 같은 종류 책을 여러 책장에 나눠 꽂는 작업이 샤딩이고, 책마다 어느 책장인지 색인을 다시 만들어야 해서 2주가 걸려요.”
- “지금 안 하면 3개월 뒤 책장이 넘쳐 주문이 멈춥니다” (영향 중심)
- 간단한 before/after 도식 1장
결과:
- PM이 즉시 이해하고 2주 지연을 우선순위로 수용
- 이후 인프라 작업 설명 때도 비유 라이브러리가 쌓여 소통 비용 감소
교훈: 기술 용어는 벽이고 비유는 문이다. 비개발자에겐 “이게 무엇인지” 가 아니라 “이게 왜 중요한지(안 하면 무슨 일이 나는지)” 를 일상 비유로 전달한다. 이해시키는 게 내 일정을 지키는 협업 기술.
마무리: 시니어에게 기대하는 것
- Trade-off 사고: “정답”보다 “상황에 맞는 선택”
- 실패 경험: 장애 대응, 잘못된 설계 경험에서 배운 점
- 설명 능력: 복잡한 개념을 쉽게 풀어서 설명
- 오너십: 문제를 끝까지 해결하려는 태도
- 성장 마인드셋: 모르는 건 인정하고 배우려는 자세
면접에서 모든 질문에 완벽하게 대답할 필요 없습니다. 생각하는 과정과 경험에서 우러나온 인사이트가 더 중요합니다.
시리즈 전체 정리
| 편 | 주제 | 핵심 키워드 |
|---|---|---|
| 1편 | 인프라/스케일링 | 트래픽, 캐싱, 샤딩, 분산락, CAP |
| 2편 | 운영/안정성 | 서킷브레이커, Kafka, 배포, 모니터링 |
| 3편 | 설계/리더십 | ADR, 시스템디자인, 코드리뷰, 멘토링 |
이 세 가지를 균형 있게 갖춘 개발자가 진짜 시니어입니다.