백엔드 면접 학습 노트 목차

신입·인턴 실전 기출 — 100문항·모범답안

13편은 채용공고 기반이라 10개사 중 9개가 경력직이었다. 이 편은 실제 신입·인턴 면접에서 받은 질문을 후기에서 모으고, 면접장에서 그대로 말할 수 있는 답변을 전 문항에 붙였다.

각 문항은 이렇게 구성된다.

Q  실제로 나온 질문
A  이렇게 답한다 — 실제로 말할 문장
   왜 그렇게 답하는가 — 근거와 메커니즘
↳  꼬리질문과 그 답
✕  이렇게 답하면 감점

구성은 신입 면접의 실제 비중대로 인성·프로젝트가 앞에 온다. 조사 시점 2026-08. 출처는 맨 아래.


신입·인턴 면접의 실제 형태

후기에서 반복되는 모양은 이렇다.

서류 → 코딩테스트 또는 과제 → 기술면접(+인성) → (인턴은) 전환 평가

실제 인턴 면접 한 건의 구성이 그대로 남아 있다.

1회 대면 · 1시간 40분 · 면접관은 CTO 와 메인 개발자들
배점    구현 과제 30% · 포트폴리오 60% · 역질문 10%
과제    일주일 예정이었지만 실제 준비 시간은 이틀 미만

포트폴리오가 60%다. 신입·인턴 면접의 무게는 CS 암기가 아니라 "네가 만든 것을 네가 설명할 수 있는가" 에 있다. 아래 B절이 그래서 가장 중요하다.


A. 자기소개·지원동기·인성 — 신입 면접의 절반

Q1. 짧은 자기소개를 부탁드립니다.

A. "안녕하세요. Java·Spring 으로 서버를 만들어 온 ○○○입니다. 최근에는 주문 API 를 만들면서 동시에 같은 상품을 주문했을 때 재고가 음수가 되는 문제를 겪었고, 원자적 UPDATE 로 해결하면서 트랜잭션과 락을 실제로 이해하게 됐습니다. 지금은 그때 배운 걸 테스트 코드로 남기는 데 관심이 많습니다. 잘 부탁드립니다."

왜 그렇게 답하는가 — 자기소개의 목적은 면접관이 다음 질문을 만들 재료를 주는 것이다. 위 답에는 재고·동시성·테스트 세 개의 갈고리가 있고, 어느 쪽으로 질문이 와도 준비된 이야기로 이어진다. 30초 버전과 1분 버전을 따로 만들어 둔다.

↳ "그 재고 문제를 어떻게 해결했나요?" → B절 Q15 로 이어진다. 이 갈고리를 던졌다면 그 답을 준비해야 한다.

✕ 감점 — "성실하고 열정적입니다" 같은 형용사만 있는 소개. 검증할 수 없어서 다음 질문이 안 나온다.

Q2. 저희 회사에 대해 알고 계신 것이 있을까요? 자사 앱·사이트를 사용해보셨나요?

A. "네, 지난주에 가입해서 ○○ 기능까지 써봤습니다. 상품 목록에서 필터를 바꿀 때 응답이 눈에 띄게 빨라서 캐시나 검색 엔진을 쓰시는지 궁금했습니다. 반대로 주문 내역 화면은 항목이 많아질 때 조금 느려지는 것 같아서, 제가 만든 프로젝트에서 겪은 N+1 문제가 떠올랐습니다."

왜 그렇게 답하는가 — 이 질문에 답을 못 하는 순간 지원 동기 전체가 무너진다. 서비스 회사라면 안 써 본 것이 곧 결격이다. 관찰 → 기술적 추측 → 내 경험 연결 세 단계로 만들면 곧바로 기술 대화가 시작된다.

↳ "느리다고 느낀 화면을 개선한다면 어떻게 하시겠어요?" → 실제로 자주 이어지는 질문이다. "측정부터 하겠습니다. 쿼리 로그로 N+1 인지 보고, 맞으면 fetch join 을, 아니면 인덱스와 실행 계획을 확인하겠습니다" 처럼 순서로 답한다.

✕ 감점 — "혁신적인 기업이라고 알고 있습니다" 같은 채용 페이지 문구 반복. 안 써 봤다는 뜻으로 읽힌다.

Q3. 개발자가 되고 싶었던 이유, 백엔드를 하게 된 계기는?

A. "처음에는 화면을 만드는 게 재미있어서 시작했는데, 동아리 회비 정산 시트를 웹으로 옮기면서 계산이 틀리면 안 되는 부분이 서버에 있다는 걸 알게 됐습니다. 눈에 보이는 화면보다 보이지 않는 규칙과 데이터가 맞아떨어질 때 더 재미를 느꼈고, 그래서 백엔드를 택했습니다."

왜 그렇게 답하는가 — 계기는 취향 진술이 아니라 경험 서술이어야 한다. "돈을 잘 벌어서", "전망이 좋아서" 는 지원 동기와 연결되지 않는다.

✕ 감점 — 프론트엔드를 낮추는 방식으로 백엔드를 고른 이유를 설명하는 것. 협업 태도로 읽힌다.

Q4. 저희 회사에 다니고 싶은 이유가 듣고 싶습니다.

A. "주문·정산처럼 틀리면 바로 문제가 되는 도메인을 다루는 회사에서 일하고 싶었습니다. 제가 만든 프로젝트에서 재고와 결제 상태가 어긋나는 문제를 겪으면서, 이런 정합성 문제를 제대로 다루는 팀에서 배우고 싶다는 생각이 확실해졌습니다. 채용 공고에 있던 ○○ 도 그래서 눈에 들어왔습니다."

왜 그렇게 답하는가 — "성장하고 싶습니다" 는 어느 회사에나 붙어서 정보량이 0이다. 그 회사의 도메인·기술 스택·규모 중 최소 하나를 걸어야 한다.

✕ 감점 — 회사를 학원으로 취급하는 답("많이 배울 수 있을 것 같아서"). 기여 의사가 안 보인다.

Q5. 자신은 어떤 사람이라고 생각하나요? 강점과 약점은?

A. "기록으로 문제를 줄이는 편입니다. 팀 프로젝트에서 같은 오류를 두 번 겪고 나서 트러블슈팅 노트를 쓰기 시작했고, 그 뒤로 같은 문제에 쓰는 시간이 확실히 줄었습니다. 약점은 처음부터 완벽하게 만들려다 진도가 느려지는 것이었습니다. 지금은 기능 목록을 먼저 쪼개고 동작하는 것부터 만든 뒤 리팩터링하는 순서로 바꿨습니다."

왜 그렇게 답하는가 — 약점은 고치고 있는 증거와 함께 말해야 자기 인식으로 읽힌다. "완벽주의라 약점이 장점입니다" 식의 포장은 즉시 간파된다.

↳ "그 노트에서 가장 기억나는 항목이 뭔가요?" → 실제로 하나를 준비해 둔다.

✕ 감점 — 치명적 약점을 그대로 말하는 것("꼼꼼하지 못해 실수가 많습니다" 로 끝). 개선 서술이 없으면 위험 신호다.

Q6. 팀원들과 불화가 있었을 때 어떻게 해결했나요?

A. "코드 리뷰에서 서로 스타일을 지적하다 감정이 상한 일이 있었습니다. 원인이 사람이 아니라 기준이 없어서라고 봤습니다. 그래서 회의에서 컨벤션 문서를 만들고 포매터를 CI 에 넣자고 제안했고, 그 뒤로는 리뷰가 로직 이야기만 하게 됐습니다."

왜 그렇게 답하는가 — 협업 질문의 평가 대상은 갈등의 내용이 아니라 태도와 해결 방식이다. "규칙·도구로 재발을 막았다" 는 구조적 해결이 가장 좋은 답이다. 사실 → 내 행동 → 결과 순서로 말한다.

✕ 감점 — 상대를 탓하는 서술. "팀원이 실력이 부족해서" 로 시작하면 그 자리에서 끝난다.

Q7. 팀 리더를 하면서 어려웠던 점과 해결 방법은?

A. "일정이 밀리는 걸 늦게 아는 게 가장 어려웠습니다. 그래서 주 2회 15분 스탠드업을 넣어 막힌 지점을 하루 안에 드러나게 했고, 막힌 사람에게는 작업을 쪼개서 다시 배분했습니다. 결과적으로 마감 3일 전에 기능 동결을 지킬 수 있었습니다."

왜 그렇게 답하는가 — "일정 조정·컨디션 관리를 했다" 고 적으면 면접관은 어떻게 했는지를 묻는다. 주기·시간·기간 같은 숫자를 넣으면 실제로 한 일로 들린다.

✕ 감점 — "제가 다 했습니다". 리더십이 아니라 분배 실패로 읽힌다.

Q8. 모르는 것이 생겼을 때 어떻게 해결하나요?

A. "먼저 에러 메시지를 그대로 읽고 최소 재현 코드를 만듭니다. 그다음 공식 문서와 스택트레이스가 가리키는 라이브러리 코드를 봅니다. 그래도 안 되면 제가 확인한 것과 시도한 것, 막힌 지점을 정리해서 질문합니다. 혼자 붙잡는 시간은 30분에서 1시간으로 제한합니다."

왜 그렇게 답하는가 — 이 질문의 실제 관심은 "질문 전에 무엇을 확인했는가" 와 "혼자 얼마나 오래 막혀 있을 사람인가" 다. 시간 상한을 말하면 협업 감각이 있다고 읽힌다.

↳ "공식 문서를 봐도 모를 때는요?" → "라이브러리 소스나 테스트 코드를 봅니다. 테스트가 사용법을 가장 정확히 보여줍니다."

✕ 감점 — "구글링합니다" 로 끝. 블로그만 본다는 뜻으로 읽힌다.

Q9. 새로운 스택을 쓰는 데 두려움은 없나요? 지금 쓰던 스택을 못 쓰게 될 수도 있는데 괜찮나요?

A. "괜찮습니다. 학교에서 배운 건 Java 였지만 프로젝트에서 필요해서 Kotlin 으로 옮겨 본 적이 있는데, 문법은 며칠이면 되고 오래 걸린 건 그 생태계의 관례였습니다. 그때 HTTP·트랜잭션·객체지향처럼 남는 개념을 기준으로 배우면 언어는 도구라는 걸 알게 됐습니다."

왜 그렇게 답하는가 — 말로만 "언어는 도구" 라고 하면 공허하다. 옮겨 본 경험 한 건이 있으면 같은 문장이 근거를 갖는다. 없으면 "무엇을 기준으로 배울지" 를 구체적으로 말한다.

✕ 감점 — "저는 Java 만 하고 싶습니다". 조직 사정에 대한 유연성 부족으로 읽힌다.

Q10. 스페셜리스트와 제너럴리스트 중 무엇이 더 중요하다고 생각하나요?

A. "신입 때는 제너럴리스트에 가깝게 넓게 보되, 하나는 깊게 파야 한다고 생각합니다. 저는 지금 DB 쪽을 깊게 보고 있습니다. 인덱스와 실행 계획을 볼 수 있게 되니 API 성능 문제도, 배포 후 장애도 원인을 좁히기 쉬워졌습니다. 넓이는 문제를 발견하는 데, 깊이는 해결하는 데 쓰인다고 봅니다."

왜 그렇게 답하는가 — 정답이 없는 질문이니 선택하고 이유를 대면 된다. 양쪽 다 중요하다는 답이 가장 나쁘다. 판단을 회피한 것으로 읽힌다.

✕ 감점 — 둘 다 중요하다고만 말하고 끝내기.

Q11. 재택근무에 대한 생각이 궁금합니다. 어떤 개발 문화를 추구하나요?

A. "저는 신입이라 옆에서 보고 배우는 시간이 필요하다고 생각해서 초반에는 출근이 더 도움이 될 것 같습니다. 문화는 코드 리뷰가 실제로 도는 팀을 바랍니다. 리뷰에서 지적받은 게 제일 빠른 학습이었습니다."

왜 그렇게 답하는가 — 회사 제도를 반대로 말하지 않도록 사전 조사가 필요하다. 문화는 추상어("자유로운") 대신 관측 가능한 행동("리뷰가 돈다", "회고를 한다")으로 말한다.

✕ 감점 — 재택 여부를 조건처럼 말하는 것. 지원 의사가 약하다고 읽힌다.

Q12. 공백기가 있는데 이유가 무엇인가요?

A. "비전공으로 시작해서 ○○개월 동안 국비 과정과 개인 프로젝트에 썼습니다. 그 기간에 만든 게 지금 포트폴리오의 프로젝트 두 개이고, 커밋 기록은 저장소에 남아 있습니다. 쉬었던 게 아니라 직무를 바꾸는 데 쓴 시간이었습니다."

왜 그렇게 답하는가 — 비전공·전환자에게는 반드시 온다. 변명이 아니라 그 기간의 산출물로 답하면 공백이 준비 기간으로 바뀐다.

✕ 감점 — "취업이 잘 안 돼서" 로 끝내기. 그 기간에 무엇을 했는지가 없으면 공백이 그대로 남는다.


B. 프로젝트 파고들기 — 배점이 가장 큰 곳

Q13. 프로젝트에 대해 간단하게 설명해주세요.

A. "동아리 물품 대여 서비스입니다. 카톡으로 신청을 받다 보니 중복 대여와 반납 누락이 잦았던 게 문제였습니다. 대여 가능 여부를 서버가 판정하게 만들고, 반납 예정일 알림을 붙였습니다. 저는 대여·반납 API 와 DB 스키마를 맡았고, 실제로 동아리 40명이 한 학기 썼습니다. 중복 대여 문의가 사라진 게 가장 큰 결과였습니다."

왜 그렇게 답하는가문제 → 해결 → 내 역할 → 결과 네 칸을 30초 안에 채운다. "Spring Boot, JPA, MySQL, Docker 를 사용했습니다" 로 시작하면 무엇을 해결했는지가 사라진다. 기술 스택은 이어지는 질문에서 자연히 나온다.

↳ "규모가 작은데 그 정도로 서버가 필요했나요?" → "규모보다 판정 주체가 문제였습니다. 사람이 눈으로 확인하면 중복이 나고, 서버가 유일성을 보장하면 안 납니다" 처럼 본질로 답한다.

✕ 감점 — 기술 나열로 시작. 그리고 "쇼핑몰 클론" 처럼 문제 정의가 없는 프로젝트를 그대로 설명하는 것.

Q14. 그 기술을 왜 선택했나요? (Redis 를 왜 썼나요 / 이 라이브러리를 왜 썼나요)

A. "처음에는 세션을 서버 메모리에 뒀는데, 서버를 두 대로 늘리자 로그인이 자꾸 풀렸습니다. 요청이 다른 서버로 가면 세션이 없으니까요. 그래서 세션 저장소를 밖으로 빼야 했고, DB 에 넣으면 매 요청마다 조회가 생기니 만료 처리까지 지원하는 Redis 를 선택했습니다. TTL 을 주면 만료를 직접 관리하지 않아도 되는 게 결정적이었습니다."

왜 그렇게 답하는가 — 기술 선택 질문의 핵심은 "어떤 문제가 있었고 대안과 비교해 왜 이것인가" 다. "빠르니까", "많이 쓰니까" 는 답이 아니다. 위 답에는 문제(세션 유실) · 원인(인스턴스 간 비공유) · 대안(DB) · 결정 근거(TTL) 가 다 있다.

↳ "Redis 가 죽으면 어떻게 되나요?" → "전원 로그아웃이 됩니다. 그래서 실제 서비스라면 복제 구성을 두거나, 세션 대신 짧은 만료의 토큰으로 바꿔 가용성 영향을 줄이는 선택을 고려하겠습니다."

✕ 감점 — "요즘 많이 쓰는 기술이라서". 유행으로 선택했다는 자백이다.

Q15. 구현 중 가장 어려웠던 점과 해결 방법은?

A. "동시에 같은 상품을 주문하면 재고가 음수가 되는 문제였습니다. 처음엔 조회해서 if (stock >= qty) 로 검사하고 저장했는데, 두 요청이 같은 값을 읽으면 둘 다 통과했습니다. 재현 테스트를 CountDownLatch스레드 10개를 동시에 던져 만들었고, 검사와 차감을 한 문장의 원자적 UPDATE 로 바꿨습니다.

UPDATE product SET stock = stock - :qty
 WHERE id = :id AND stock >= :qty;
-- 영향 행 수가 0이면 재고 부족 → 예외로 처리

이렇게 하면 조건 검사와 차감이 DB 안에서 한 번에 일어나서 경쟁 조건이 사라집니다."

왜 그렇게 답하는가 — 이 답의 가치는 결과가 아니라 원인을 찾은 과정에 있다. 증상 → 가설 → 재현 → 조치 → 확인 순서로 말하면 그 자체가 실력의 증거다. 특히 재현 테스트를 만들었다는 부분이 신입에게서 보기 드문 신호다.

↳ "락으로 풀 수도 있는데 왜 UPDATE 로 했나요?" → "비관적 락도 정답입니다. 다만 재고 차감은 조건과 갱신이 한 문장으로 표현되니 락 없이 원자성이 확보되고, 잠금 대기가 없어 동시성이 더 낫다고 판단했습니다."

↳ "서버가 여러 대여도 안전한가요?" → "네, 판정이 DB 한 곳에서 일어나니 인스턴스 수와 무관합니다."

✕ 감점 — "synchronized 를 붙여 해결했습니다" 로 끝내는 것. 한 JVM 안에서만 유효해서 서버가 두 대가 되면 다시 깨진다는 걸 모르는 답이다.

Q16. 프로젝트 아키텍처를 설명해주세요.

A. "요청이 들어오면 Controller 가 검증과 변환만 하고, Service 가 트랜잭션 경계와 규칙을 갖고, Repository 가 DB 접근을 담당합니다. 대여 가능 여부 판정 같은 규칙은 Service 와 도메인 객체에 두고, Controller 에는 두지 않았습니다. 이렇게 나눈 이유는 규칙을 테스트할 때 웹 계층 없이 테스트할 수 있게 하려는 것이었습니다."

왜 그렇게 답하는가 — 계층 이름을 나열하는 게 아니라 경계와 책임, 그리고 왜 나눴는지를 말한다. "테스트 때문에 나눴다" 는 이유가 가장 설득력 있다.

↳ "그럼 Controller 에 로직이 들어가면 뭐가 문제인가요?" → "같은 규칙을 배치나 다른 API 에서 재사용할 수 없고, 테스트에 웹 컨텍스트가 필요해집니다."

✕ 감점 — 계층 이름만 대고 어디에 무엇을 뒀는지 말하지 못하는 것.

Q17. 협업하면서 어려웠던 경험과 해결 방법은?

A. "API 스펙이 자주 바뀌어서 프론트와 계속 어긋났습니다. 말로 전달하다 보니 누락이 생겼고, 그래서 Swagger 로 문서를 코드에서 생성하게 바꿨습니다. 그 뒤로는 스펙이 바뀌면 문서가 같이 바뀌어서 '어느 게 최신이냐' 는 질문이 사라졌습니다."

왜 그렇게 답하는가 — A6 과 달리 개발 협업 맥락(브랜치 충돌·스펙 변경·리뷰 지연)에서 답한다. 도구로 해결한 이야기는 재발 방지까지 보여 준다.

✕ 감점 — "특별히 어려운 건 없었습니다". 협업을 안 해봤다는 뜻으로 읽힌다.

Q18. 팀 프로젝트에서 본인의 기여도는 어느 정도였나요?

A. "네 명이었고 저는 대여·반납 API 와 스키마 설계, 그리고 CI 설정을 맡았습니다. 비율로 말하기보다, 제가 만든 부분은 지금 이 자리에서 코드 흐름을 설명할 수 있습니다. 반납 로직은 특히 예외 케이스가 많아서 테스트를 12개 썼습니다."

왜 그렇게 답하는가 — 퍼센트로 과장하면 꼬리질문에서 반드시 무너진다. "내가 만든 모듈을 지목하고 그 안을 설명할 수 있다" 가 가장 안전하고 강한 답이다.

↳ "그 반납 로직에서 예외 케이스가 뭐였나요?" → 실제로 두세 개를 즉시 말할 수 있어야 한다.

✕ 감점 — "80% 정도 했습니다" 라고 말한 뒤 남의 코드를 설명 못 하는 것.

Q19. 제일 자신 있는 스택은 무엇인가요?

A. "Spring Boot 와 JPA 입니다. 특히 JPA 는 N+1 을 직접 겪고 해결하면서 영속성 컨텍스트가 언제 쿼리를 만드는지까지 보게 됐습니다."

왜 그렇게 답하는가 — 말한 스택으로 곧바로 가장 깊은 질문이 온다. 그래서 자신 있는 것 하나만 말하고, 그 하나는 끝까지 답할 준비를 한다. 넓게 나열하면 가장 약한 것을 물어본다.

✕ 감점 — "Java, Spring, Python, React, Docker, K8s 다 조금씩…". 가장 약한 데를 찔린다.

Q20. 코드 컨벤션은 누가 정한 건가요? 왜 그렇게 했나요?

A. "팀에서 합의했습니다. 처음에는 각자 스타일로 쓰다가 리뷰가 스타일 지적으로 채워졌고, 그래서 구글 자바 스타일을 기준으로 삼고 포매터를 CI 에 넣었습니다. 사람이 판단하지 않게 만드는 게 목적이었습니다."

왜 그렇게 답하는가 — 실제 후기에서 나온, 조금 의외인 질문이다. 보는 것은 팀 규칙을 어떻게 합의하고 강제했는가 다. "누가 정해줬다" 보다 "합의하고 자동화했다" 가 좋다.

✕ 감점 — "특별한 컨벤션은 없었습니다". 협업 경험의 밀도가 낮게 읽힌다.

Q21. 개발 일정을 어떻게 산정하나요? 일정이 길다고 하면 비개발 직군에 어떻게 설명하나요?

A. "기능을 하루 안에 끝낼 수 있는 단위로 쪼개서 합산하고, 불확실한 항목에는 버퍼를 둡니다. 비개발 직군에는 '오래 걸린다' 가 아니라 무엇 때문에 걸리는지를 기능 단위로 설명합니다. 예를 들어 '결제 연동은 실패·환불 케이스 처리가 절반이라 화면 하나로 보이지만 작업은 다섯 개다' 처럼요. 그리고 먼저 쓸 수 있는 범위를 먼저 내보내는 안을 같이 제시합니다."

왜 그렇게 답하는가 — 이 질문은 견적 능력보다 설명과 협상 능력을 본다. "쪼갠다 → 근거를 준다 → 대안을 준다" 세 단계로 답한다.

✕ 감점 — "정확히 산정하기 어렵습니다" 로 끝내기.

Q22. 리팩터링을 해본 적 있나요? 언제 해야 한다고 생각하나요?

A. "같은 코드를 세 번째 복사하려 할 때기능 추가가 여러 파일을 동시에 건드리게 될 때 신호라고 봅니다. 저는 대여·반납에서 중복된 검증 로직을 도메인 객체로 모은 적이 있습니다. 전제 조건은 테스트가 먼저 있는 것입니다. 테스트 없이 구조를 바꾸면 리팩터링이 아니라 재작성이 됩니다."

왜 그렇게 답하는가 — "코드가 더러워서" 는 취향이고, "변경이 어려워졌을 때" 는 기준이다. 테스트 선행을 언급하는 것이 신입 답변에서 특히 좋은 인상을 준다.

↳ "리팩터링과 재작성의 차이는요?" → "동작을 바꾸지 않는다는 보장이 있는지의 차이입니다. 그 보장이 테스트입니다."

✕ 감점 — "시간 남으면 합니다". 우선순위 감각이 없다고 읽힌다.


C. CS 기초 — 짧고 정확하게, 그리고 "언제 쓰냐"까지

Q23. 1 byte 는 몇 bit 인가요?

A. "8 bit 입니다."

왜 그렇게 답하는가 — 실제로 비전공자 면접에서 나온 첫 질문이다. 이런 걸 틀리면 뒤의 답변 신뢰도가 전부 흔들린다. 여유가 있으면 한 줄 덧붙인다 — "그래서 int 는 4 byte, 32 bit 라서 약 ±21억까지 담습니다."

↳ "그럼 int 로 금액을 다루면 어떤 문제가 생기나요?" → "21억을 넘는 금액에서 오버플로가 납니다. 그래서 금액은 long 이나 BigDecimal 을 씁니다. 특히 소수점이 있으면 double 은 이진 부동소수라 0.1 을 정확히 표현하지 못해 BigDecimal 을 써야 합니다."

✕ 감점 — "잘 모르겠습니다". 기본 단위는 반드시 답해야 한다.

Q24. 프로세스와 스레드의 차이를 설명해주세요.

A. "프로세스자기만의 주소 공간을 가진 실행 단위이고, 스레드그 안에서 도는 실행 흐름입니다. 같은 프로세스의 스레드들은 코드·데이터·힙을 공유하고 스택과 레지스터는 각자 가집니다. 그래서 스레드 전환이 더 싸고 데이터 공유가 쉽지만, **공유 자원에 동시 접근하면 경쟁 조건**이 생겨서 동기화가 필요합니다."

왜 그렇게 답하는가 — "공유하는 것과 각자 갖는 것" 을 구분해 말하는 것이 이 질문의 핵심이다. 거기서 곧바로 동기화 질문이 이어지니 미리 연결해 두면 흐름을 내가 만든다.

↳ "그럼 멀티 프로세스와 멀티 스레드는 언제 각각 쓰나요?" → "격리가 중요하면 프로세스입니다. 하나가 죽어도 다른 게 살아 있어야 할 때요. 자원 공유와 전환 비용이 중요하면 스레드입니다. 웹 서버가 요청을 스레드로 처리하는 게 후자입니다."

✕ 감점 — "프로세스는 무겁고 스레드는 가볍다" 만 말하기. 무엇이 무거운지가 없다.

Q25. 컨텍스트 스위칭은 언제 발생하고 왜 비싼가요?

A. "타임 슬라이스가 끝났을 때, I/O 를 기다려 블로킹될 때, 더 높은 우선순위 작업이 깨어났을 때 발생합니다. 비용은 레지스터와 스택 포인터를 저장·복원하는 직접 비용도 있지만, 더 큰 건 간접 비용입니다. 새 작업이 올라오면 CPU 캐시와 TLB 에 있던 이전 작업의 내용이 쓸모없어져서 한동안 캐시 미스가 계속 납니다."

왜 그렇게 답하는가 — 신입 답변은 대개 "저장·복원 비용" 에서 멈춘다. 캐시·TLB 오염을 말하면 한 단계 깊은 답이 된다.

↳ "그럼 오버헤드를 줄이려면요?" → "스레드 수를 코어 수에 맞춰 과도한 경쟁을 줄이고, I/O 대기가 많으면 비동기·논블로킹으로 바꿔 대기 중 전환 자체를 없애는 방향입니다."

Q26. 동기화가 필요한 이유와 뮤텍스·세마포어의 차이는?

A. "여러 스레드가 같은 자원을 동시에 바꾸면 **결과가 실행 순서에 따라 달라지는 경쟁 조건**이 생기기 때문입니다. 뮤텍스는 소유자가 있는 잠금이라 잠근 스레드만 풀 수 있고 동시 진입은 한 개입니다. 세마포어는 카운트여서 정해진 개수만큼 동시 진입을 허용하고, 소유 개념이 없어 다른 스레드가 올릴 수 있습니다."

왜 그렇게 답하는가 — 차이의 본질은 개수가 아니라 소유권이다. 그래서 우선순위 상속이나 재귀 잠금 같은 기능이 뮤텍스에만 있다.

↳ "실무에서 세마포어는 어디에 쓰나요?" → "동시 실행 개수를 제한할 때입니다. 외부 API 호출을 동시에 5개까지만 허용하거나, 커넥션 풀처럼 자원 개수를 제한하는 경우입니다."

↳ "synchronizedReentrantLock 의 차이는요?" → "synchronized 는 블록을 벗어나면 자동 해제되지만 대기 중 인터럽트나 타임아웃을 줄 수 없습니다. ReentrantLocktryLock(timeout) 처럼 포기할 수 있는 대기가 가능해서 데드락 회피에 유리합니다."

Q27. 데드락의 조건과 예방법은?

A. "상호 배제, 점유하며 대기, 비선점, 환형 대기 네 가지가 동시에 성립할 때 생깁니다. 하나만 깨면 데드락이 안 생기고, 실무에서 가장 현실적인 방법은 잠금 획득 순서를 고정하는 것입니다. A 다음 B 라는 순서를 모두가 지키면 환형 대기가 만들어지지 않습니다. 그다음이 타임아웃을 걸어 포기하고 재시도하는 방법입니다."

왜 그렇게 답하는가 — 네 조건을 외워 나열한 뒤 "그래서 무엇을 깰 것인가" 로 이어가야 실무 감각이 있다고 읽힌다. 이 질문은 DB 데드락으로 자연스럽게 확장된다.

↳ "DB 에서 데드락이 나면요?" → "InnoDB 는 대기 그래프에서 순환을 감지하면 비용이 적은 트랜잭션을 골라 롤백합니다. 애플리케이션은 그 예외를 잡아 재시도할 수 있어야 합니다."

Q28. 가상 메모리가 무엇이고 왜 필요한가요? 스왑이 일어나면 왜 느려지나요?

A. "프로세스에게 연속된 자기만의 주소 공간을 주고, 실제 물리 메모리와의 대응은 페이지 테이블로 관리하는 기법입니다. 덕분에 물리 메모리보다 큰 프로그램을 돌릴 수 있고 프로세스 간 격리도 됩니다. 스왑이 느린 이유는 디스크 접근이기 때문입니다. 메모리가 수십 나노초인데 디스크는 그보다 수천~수만 배 느려서, 페이지 폴트가 잦아지면 CPU 는 계속 기다리기만 하는 스래싱 상태가 됩니다."

↳ "페이지 폴트가 나면 무슨 일이 일어나나요?" → "MMU 가 유효한 매핑을 못 찾아 트랩을 걸고, OS 가 디스크에서 해당 페이지를 올린 뒤 페이지 테이블을 갱신하고 그 명령을 다시 실행합니다."

Q29. 세션과 쿠키의 차이를 설명해주세요.

A. "쿠키는 브라우저에 저장되는 값이고, 세션서버에 상태를 저장하고 그 식별자만 쿠키로 주고받는 방식입니다. 그래서 세션은 서버가 상태를 갖게 되고, 서버가 여러 대면 그 상태를 공유해야 합니다. 제 프로젝트에서 서버를 두 대로 늘렸을 때 로그인이 풀린 게 정확히 이 문제였습니다."

왜 그렇게 답하는가 — 정의만 말하면 거기서 끝나지만, 확장 시 문제까지 말하면 Redis·JWT 이야기로 내가 흐름을 만들 수 있다.

↳ "그럼 어떻게 해결하나요?" → "세션 저장소를 Redis 처럼 밖으로 빼거나, 로드밸런서에서 같은 사용자를 같은 서버로 보내는 스티키 세션을 쓰거나, 아예 무상태 토큰으로 바꿉니다. 스티키 세션은 서버가 죽으면 그 사용자들만 로그아웃되는 약점이 있습니다."

Q30. 브라우저에 google.com 을 입력하면 무슨 일이 일어나나요?

A. "① DNS 로 IP 를 찾습니다 — 브라우저 캐시, OS 캐시, hosts, 그다음 리졸버가 루트 → TLD → 권한 서버 순으로 물어봅니다. ② 그 IP 로 TCP 3-way 핸드셰이크를 하고, HTTPSTLS 핸드셰이크로 키를 교환합니다. ③ HTTP 요청을 보내고 서버가 응답합니다. ④ 브라우저가 HTML 을 파싱하며 CSS·JS·이미지를 추가로 요청해 렌더링합니다. 각 단계마다 캐시가 있어서, 실제로는 대부분 중간에서 끝납니다."

왜 그렇게 답하는가 — 단골 질문이고, 면접관이 원하는 건 계층을 빠뜨리지 않는 것이다. DNS → TCP → TLS → HTTP → 렌더 다섯 단계를 순서대로 말하고, 각 단계에서 꼬리질문이 오면 그때 깊이 들어간다.

↳ "같은 도메인인데 사람마다 다른 IP 가 나오는 이유는요?" → "지리적으로 가까운 서버로 보내려고 DNS 가 요청자 위치에 따라 다른 레코드를 주기 때문입니다. CDN 이 이 방식으로 동작합니다."

Q31. HTTP 와 HTTPS 의 차이는?

A. "HTTPS 는 HTTP 아래에 TLS 계층을 둬서 세 가지를 얻습니다. 기밀성(도청 불가), 무결성(변조 감지), 서버 인증(내가 접속한 곳이 그 도메인이 맞는지). 인증서는 이 공개키가 이 도메인의 것임을 CA 가 보증하는 문서입니다."

↳ "그럼 암호화는 공개키로 하는 건가요?" → "아닙니다. 공개키는 대칭키를 안전하게 합의하는 데만 쓰고, 실제 데이터는 대칭키로 암호화합니다. 비대칭이 훨씬 느리기 때문입니다."

✕ 감점 — "HTTPS 는 암호화된 HTTP 입니다" 로 끝내기. 인증 기능을 빠뜨린 답이다.

Q32. TCP 와 UDP 의 차이, 3-way handshake 를 설명해주세요.

A. "TCP연결형이고 순서 보장·재전송·흐름 제어를 해줍니다. UDP비연결형으로 그런 보장 없이 바로 보내서 가볍고 지연이 적습니다. 핸드셰이크는 SYN → SYN+ACK → ACK 인데, 목적은 양쪽이 서로의 초기 시퀀스 번호를 확인하는 것입니다."

↳ "왜 2-way 로는 안 되나요?" → "2번으로는 한쪽 방향만 확인됩니다. 서버가 보낸 SYN+ACK 가 도착했는지를 서버는 알 수 없어서, 클라이언트의 마지막 ACK 로 양방향이 모두 살아 있음을 확인하는 것입니다."

↳ "실시간 게임이 UDP 를 쓰는 이유는요?" → "늦게 도착한 위치 정보는 이미 쓸모가 없기 때문입니다. 재전송을 기다리는 것보다 다음 최신 값을 받는 게 낫다면 UDP 가 맞습니다."

Q33. 알고 있는 자료구조를 설명해주세요. 큐와 스택은 언제 각각 쓰나요?

A. "배열·연결 리스트·스택·큐·해시 테이블·트리를 주로 씁니다. 스택은 LIFO 라서 되돌리기, 괄호 검사, 재귀·DFS 에 씁니다. 큐는 FIFO 라서 작업 대기열, BFS 에 씁니다. 차이의 핵심은 가장 최근 것을 먼저 볼 것인가, 먼저 온 것을 먼저 처리할 것인가입니다."

왜 그렇게 답하는가 — 정의만 말하면 암기로 읽힌다. "어디에 쓰냐" 를 붙이는 것이 이 질문에서 가장 큰 차이를 만든다.

↳ "해시 테이블의 시간 복잡도는요? 최악에는요?" → "평균 O(1), 최악은 O(n) 입니다. 해시 충돌이 몰리면 한 버킷에 다 들어가서요. Java 의 HashMap 은 한 버킷이 길어지면 연결 리스트를 트리로 바꿔 O(log n) 으로 완화합니다."

Q34. 회사는 왜 백엔드 개발자에게 코딩테스트로 자료구조·알고리즘을 확인할까요?

A. "짧은 시간에 문제를 코드로 옮기는 기본기복잡도를 의식하는지를 볼 수 있기 때문이라고 생각합니다. 실무에서 직접 정렬을 구현하는 일은 드물지만, 어떤 자료구조를 고르느냐가 성능을 결정하는 상황은 자주 있습니다. 저도 조회가 느린 코드를 리스트 순회에서 Map 조회로 바꿔 해결한 적이 있습니다."

✕ 감점 — "실무에서는 안 쓰는데 왜 보는지 모르겠습니다". 태도 문제로 기록된다.


D. 데이터베이스 — 신입에게 가장 깊게 파는 영역

Q35. 정규화를 설명해주세요. 비정규화는 무엇인가요?

A. "중복을 없애서 이상 현상을 막는 설계입니다. 같은 값이 여러 곳에 있으면 하나만 고쳐지는 갱신 이상, 지우면 다른 정보까지 사라지는 삭제 이상이 생깁니다. 1NF 는 원자값, 2NF 는 부분 함수 종속 제거, 3NF 는 이행 종속 제거입니다. 비정규화는 조회 성능을 위해 의도적으로 중복을 허용하는 것입니다. 정규화를 안 한 것과는 다릅니다 — 대가를 알고 선택한 것이니까요."

↳ "그럼 언제 비정규화하나요?" → "조인이 너무 많아 조회가 병목이고, 그 값이 자주 바뀌지 않을 때입니다. 게시글에 댓글 수를 컬럼으로 들고 있는 게 전형적인 예입니다. 대신 갱신 시점에 정합성을 맞출 책임이 애플리케이션으로 옮겨옵니다."

✕ 감점 — 1NF~3NF 정의만 외워 나열하고 이상 현상을 설명 못 하는 것.

Q36. 인덱스는 왜 조회를 빠르게 하나요? 많이 만들면 어떤 문제가 생기나요?

A. "인덱스정렬된 B+Tree 라서 전체를 훑지 않고 탐색 범위를 로그 단위로 좁힐 수 있습니다. 책의 색인과 같습니다. 단점은 쓰기 비용입니다. INSERT·UPDATE·DELETE 마다 인덱스도 같이 갱신해야 하고, 저장 공간도 늘어납니다. 그래서 쓰기가 많은 테이블에 인덱스를 많이 걸면 전체 성능이 떨어집니다."

왜 그렇게 답하는가 — "인덱스는 많을수록 좋다" 는 오해를 스스로 깨는 게 이 질문의 핵심이다. 읽기 이득과 쓰기 비용의 교환으로 설명한다.

↳ "인덱스가 있는데도 안 타는 경우가 있나요?" → 다음 질문으로 이어진다.

Q37. 인덱스를 타지 않는 쿼리 패턴은?

A. "네 가지를 주로 봅니다. ① 컬럼에 함수나 연산을 씌운 경우WHERE YEAR(created_at) = 2026인덱스를 못 씁니다. created_at >= '2026-01-01' AND < '2027-01-01' 로 바꿔야 합니다. ② 선행 와일드카드LIKE '%검색어' 는 시작을 모르니 탐색을 못 좁힙니다. ③ 복합 인덱스의 선행 컬럼을 건너뛴 조건.타입이 달라 암묵 형변환이 일어나는 경우 — 문자열 컬럼에 숫자를 비교하면 그렇습니다."

왜 그렇게 답하는가 — 이건 실무에서 바로 쓰는 지식이라 신입이 답하면 인상이 강하게 남는다. ①의 개선 예시처럼 고치는 방법을 함께 말하는 게 좋다.

↳ "인덱스를 타는지 어떻게 확인하나요?" → "EXPLAIN 으로 실행 계획을 봅니다. typeALL 이면 풀 스캔, key 가 비어 있으면 인덱스를 못 쓰고 있는 것입니다."

Q38. 복합 인덱스는 왜 왼쪽 컬럼부터 순서대로 써야 하나요?

A. "INDEX(A, B, C)A 로 정렬하고, A 가 같은 것 안에서 B, 그 안에서 C 로 정렬된 구조입니다. 전화번호부가 성 → 이름 순으로 정렬된 것과 같아서, 성을 모르고 이름만으로는 찾을 수 없습니다. 그래서 A 조건이 없으면 인덱스 탐색을 시작할 지점이 없습니다."

↳ "WHERE A = ? AND C = ?INDEX(A,B,C) 를 쓸 수 있나요?" → "부분적으로만 씁니다. A 까지는 인덱스로 범위를 좁히지만, B 가 빠져서 C 는 정렬이 보장되지 않아 탐색에는 못 쓰고 걸러내는 데만 쓰입니다."

↳ "그럼 컬럼 순서는 어떻게 정하나요?" → "등호 조건으로 자주 쓰는 컬럼을 앞에, 범위 조건은 뒤에 둡니다. 범위 조건 뒤의 컬럼은 정렬이 깨져서 탐색 효율이 떨어지기 때문입니다."

Q39. 트랜잭션과 ACID 를 설명해주세요.

A. "트랜잭션은 전부 반영되거나 전부 취소되어야 하는 작업 묶음입니다. 원자성은 중간에 실패해도 반쪽만 반영되는 걸 막고, 일관성은 제약 조건을 항상 만족하게 하고, 격리성은 동시 실행 트랜잭션이 서로의 중간 상태를 보지 않게 하고, 지속성은 커밋한 것은 장애가 나도 남게 합니다."

왜 그렇게 답하는가 — 네 글자를 외워 말하는 것과 각각이 막아 주는 문제로 말하는 것은 전혀 다르게 들린다. 후자가 이해한 사람의 말투다.

↳ "커밋했는데 서버가 바로 죽어도 데이터가 남는 이유는요?" → "커밋 시점에 변경 내용을 로그(WAL/redo)에 먼저 순차 기록하고 디스크에 내려쓰기 때문입니다. 데이터 파일 반영은 나중이어도, 재시작할 때 로그를 다시 적용해 복구합니다."

Q40. 격리 수준 4가지와 각 수준에서 생기는 문제는?

A. "READ UNCOMMITTED 는 커밋 안 된 값을 읽어 더티 리드가 생깁니다. READ COMMITTED 는 커밋된 것만 읽지만, 같은 조회를 두 번 하면 값이 달라질 수 있습니다. REPEATABLE READ 는 그 반복 읽기를 보장하지만 새로 삽입된 행이 보이는 팬텀이 남습니다. SERIALIZABLE 은 모두 막지만 동시성이 가장 떨어집니다. MySQL InnoDB 의 기본은 REPEATABLE READ, PostgreSQL 은 READ COMMITTED 입니다."

왜 그렇게 답하는가 — 두 엔진의 기본값이 다르다는 걸 아는 신입은 드물다. 이 한 문장이 "책만 본 것이 아니라 실제로 써봤다" 는 신호가 된다.

↳ "그럼 실무에서는 주로 무엇을 쓰나요?" → "대개 기본값을 그대로 씁니다. 필요한 곳만 SELECT ... FOR UPDATE낙관적 락으로 국소적으로 강하게 잡는 게 전체 격리 수준을 올리는 것보다 낫습니다."

Q41. 트랜잭션을 길게 잡으면 무엇이 문제인가요?

A. "세 가지입니다. 잠금 보유 시간이 길어져 다른 트랜잭션이 대기하고, 커넥션을 오래 점유해 풀이 마르고, 언두 로그가 쌓여 저장소와 조회 성능에 부담이 됩니다. 그래서 외부 API 호출은 트랜잭션 안에 두지 않습니다. 상대 서버가 느려지면 그만큼 우리 DB 커넥션이 잡혀 있게 되니까요."

왜 그렇게 답하는가 — 마지막 문장이 실무 규칙이고, 신입 면접에서 이걸 말하면 "트랜잭션을 실제로 다뤄본 사람" 으로 읽힌다.

↳ "그럼 결제 API 호출과 주문 저장을 어떻게 나누나요?" → "주문을 대기 상태로 먼저 커밋하고, 결제 호출은 트랜잭션 밖에서 하고, 결과로 상태를 갱신합니다. 실패나 응답 유실에 대비해 멱등 키를 두고 재조회로 상태를 맞춥니다."

Q42. 낙관적 락과 비관적 락의 차이, 재고 차감에는 무엇을 쓰나요?

A. "**낙관적 락**은 충돌이 드물다고 보고 잠그지 않은 채, @Version 컬럼으로 갱신 시점에 충돌을 감지해 실패시키고 재시도합니다. **비관적 락**은 SELECT ... FOR UPDATE미리 잠그는 방식입니다. 재고처럼 인기 상품에 요청이 몰리는 경우는 낙관적 락이면 재시도가 폭증하니, 저는 조건과 차감을 한 문장에 담은 원자적 UPDATE 를 씁니다. UPDATE ... SET stock = stock - :q WHERE id = :id AND stock >= :q 로 하고, 영향 행이 0이면 재고 부족으로 처리합니다."

↳ "낙관적 락에서 충돌이 나면 어떻게 처리하나요?" → "재시도합니다. 다만 몇 번까지 재시도할지 상한을 두고, 그 뒤에는 사용자에게 실패를 알립니다. 무한 재시도는 부하를 키우기만 합니다."

Q43. N+1 문제가 무엇이고 어떻게 발견·해결하나요?

A. "목록을 1번 조회한 뒤, 각 항목의 연관 엔티티를 꺼낼 때마다 쿼리가 한 번씩 더 나가서 총 1 + N 번이 되는 문제입니다. 원인은 **지연 로딩**입니다. 발견은 show_sql 이나 p6spy 로 쿼리 로그를 눈으로 세는 것이 가장 확실했습니다. 해결은 셋 중 하나입니다 — fetch join 으로 한 번에 가져오거나, @EntityGraph 로 지정하거나, default_batch_fetch_size 를 줘서 N 번을 IN 절 한 번으로 묶는 방법입니다."

왜 그렇게 답하는가 — "fetch join 을 씁니다" 로만 답하면 얕다. 원인이 지연 로딩이고 발견 방법이 있다는 것까지가 한 세트다.

↳ "그럼 다 즉시 로딩으로 바꾸면 되지 않나요?" → "안 됩니다. 필요 없을 때도 항상 조인해서 가져오게 되고, 연관이 얽히면 예상보다 훨씬 큰 쿼리가 나갑니다. 기본은 지연 로딩으로 두고, 필요한 곳에서 fetch join 하는 것이 원칙입니다."

↳ "컬렉션 fetch join 과 페이징을 같이 쓰면요?" → "행이 뻥튀기되어 페이징이 어긋납니다. 하이버네이트는 전부 메모리로 올려서 자르는 경고를 냅니다. 그럴 때는 batch size 방식으로 풉니다."

Q44. RDBMS 와 NoSQL 은 언제 쓰는 게 좋을까요?

A. "스키마가 안정적이고, 여러 테이블을 조인해서 보고, 트랜잭션 정합성이 중요하면 RDBMS 입니다. 반대로 스키마가 자주 바뀌거나, 단순 키 조회가 대부분이고, 수평 확장이 필요하면 NoSQL 이 맞습니다. 예를 들어 주문·결제는 RDBMS, 알림 로그나 세션·캐시는 다른 저장소를 씁니다. 하나만 골라야 한다면 저는 정합성이 필요한 데이터를 기준으로 결정합니다."

✕ 감점CAP 정리로 얼버무리는 것. 질문은 선택 기준이지 이론이 아니다.

Q45. In-memory DB 를 설명해주세요. Redis 를 왜 썼나요? 단점은?

A. "데이터를 디스크가 아니라 메모리에 두는 저장소라서 디스크 I/O 가 없어 훨씬 빠릅니다. 저는 세션 저장과 조회가 잦은 목록 캐시에 썼습니다. 단점은 메모리 용량이 한계라는 것과, 영속성 보장이 상대적으로 약하다는 것입니다. RDB 스냅샷이나 AOF 를 켜도 설정에 따라 마지막 몇 초가 유실될 수 있어서, 잃어도 되는 데이터에 쓰는 게 원칙입니다."

↳ "Redis 가 단일 스레드인데 왜 빠른가요?" → "명령 처리 자체가 메모리 접근이라 짧고, 단일 스레드라서 락과 컨텍스트 스위칭이 없습니다. 대신 긴 명령 하나가 전체를 막습니다. 그래서 KEYS * 같은 전체 순회 명령을 운영 중에 쓰면 안 됩니다."

↳ "메모리가 꽉 차면요?" → "maxmemory-policy 에 따라 LRU·LFU 로 키를 밀어냅니다. 캐시 용도면 이게 정상 동작이지만, 세션을 넣어 뒀다면 로그아웃이 되는 사고가 됩니다."

Q46. Redis 를 활용해 로그아웃을 어떻게 구현했나요?

A. "JWT 는 서버가 상태를 갖지 않아서 발급한 토큰을 취소할 수단이 없습니다. 그래서 로그아웃할 때 그 토큰의 식별자를 Redis 블랙리스트에 넣고, TTL 을 토큰의 남은 만료 시간으로 줬습니다. 이후 요청은 필터에서 블랙리스트를 확인하고 거부합니다. TTL 을 남은 만료로 맞추면 만료된 토큰의 기록이 저절로 사라져서 관리할 게 없습니다."

↳ "그러면 무상태라는 JWT 의 장점이 사라지지 않나요?" → "맞습니다. 그게 이 방식의 대가입니다. 그래서 실제로는 액세스 토큰의 만료를 짧게(예: 15분) 두고 리프레시 토큰으로 갱신하는 쪽을 많이 씁니다. 그러면 취소가 필요한 창이 짧아져서 블랙리스트 부담이 줄어듭니다."

왜 이 답이 좋은가 — 자기 구현의 약점을 먼저 인정하고 대안을 제시하는 것이 신입 면접에서 가장 좋은 인상을 남기는 패턴이다.

Q47. Redis 를 비전공자에게 설명한다고 생각하고 말해보세요.

A. "자주 찾는 물건을 창고까지 가지 않고 책상 위에 꺼내 두는 것과 같습니다. 책상은 좁아서 다 올려둘 수 없고, 정리하다 실수로 치울 수도 있으니 원본은 창고(DB)에 두고 사본만 올려둡니다. 그래서 책상 위 물건이 없어져도 창고에서 다시 가져오면 됩니다."

왜 그렇게 답하는가 — 설명 능력 자체를 보는 질문이다. 중요한 건 틀린 비유를 만들지 않는 것 — 위 비유는 "휘발성이라 원본이 아니다" 라는 핵심까지 담고 있다.

Q48. SQL Injection 이 무엇이고 어떻게 막나요?

A. "사용자 입력이 쿼리의 구조 자체를 바꾸는 공격입니다. WHERE id = '" + input + "' 처럼 문자열을 이어 붙이면, 입력에 ' OR 1=1 -- 을 넣어 조건을 무력화할 수 있습니다. 막는 방법은 PreparedStatement 로 값을 바인딩하는 것입니다. 바인딩하면 입력이 데이터로만 취급되어 구조에 영향을 줄 수 없습니다. JPA 나 MyBatis 를 쓰면 기본적으로 바인딩되지만, 동적 정렬 컬럼처럼 문자열을 직접 넣는 부분은 화이트리스트로 검사해야 합니다."

✕ 감점 — "특수문자를 필터링합니다" 로 끝내기. 우회 방법이 많아 근본 대책이 아니다.

Q49. ORM 이 편하고 좋은데 SQL 을 알아야 할까요?

A. "알아야 합니다. ORM 은 SQL 을 감추는 게 아니라 미루는 것이라고 생각합니다. N+1 이 생겼을 때도, 인덱스를 못 타는 쿼리가 생성됐을 때도, 결국 나간 SQL 을 읽어야 원인을 찾을 수 있었습니다. 저는 개발 중에 show_sql 을 켜두고 어떤 쿼리가 나가는지 확인하는 습관을 들였습니다."

Q50. SQL 문제를 즉석에서 드릴게요. 풀어보세요.

A. 실제로 나온다. JOIN + GROUP BY + HAVING 수준까지 손으로 쓸 수 있어야 한다. 자주 나오는 형태는 이렇다.

-- 주문이 3건 이상인 회원의 이름과 주문 건수를, 건수 많은 순으로
SELECT m.name, COUNT(*) AS cnt
  FROM member m
  JOIN orders o ON o.member_id = m.id
 GROUP BY m.id, m.name
HAVING COUNT(*) >= 3
 ORDER BY cnt DESC;

기억할 두 가지WHERE 는 그룹 전, HAVING 은 그룹 후에 걸린다. 그리고 LEFT JOIN 인데 오른쪽 테이블 조건을 WHERE 에 쓰면 INNER JOIN 이 된다. 그 조건은 ON 절에 넣어야 한다. 이게 실제 면접에서 자주 확인하는 함정이다.


E. Java · Spring · JPA

Q51. ==equals() 의 차이는?

A. "==참조가 같은지, 즉 같은 객체인지를 봅니다. equals()값이 같은지를 보는데, 재정의하지 않으면 Object 의 기본 구현이 == 와 같아서 참조 비교가 됩니다. 그래서 값 비교가 필요한 클래스는 equals 를 재정의해야 하고, 그때 hashCode 도 함께 재정의해야 합니다."

왜 그렇게 답하는가 — 마지막 문장을 스스로 붙이는 것이 중요하다. 붙이지 않으면 그게 곧 다음 질문이 되고, 붙이면 준비된 사람으로 읽힌다.

Q52. equals 만 재정의하고 hashCode 를 빼면 어떻게 되나요?

A. "HashMap·HashSet찾지 못합니다. 해시 기반 컬렉션은 먼저 hashCode 로 버킷을 고르고, 그 안에서 equals 로 비교하기 때문에, hashCode 가 다르면 아예 다른 버킷을 보게 되어 equals 가 호출될 기회조차 없습니다.

Set<Point> set = new HashSet<>();
set.add(new Point(1, 2));
set.contains(new Point(1, 2));  // equals만 재정의 → false

규약은 같으면 해시도 같아야 한다는 것이고, 반대는 성립하지 않습니다."

↳ "가변 객체를 키로 쓰면 어떤 문제가 생기나요?" → "넣은 뒤에 필드를 바꾸면 해시가 달라져서 영구히 못 찾는 원소가 됩니다. 원래 버킷에는 남아 있는데 새 해시로는 다른 버킷을 뒤지니까요. 그래서 키는 **불변 객체**로 두는 게 원칙입니다."

↳ "ListMap 의 키로 쓰면요?" → "ListhashCode 는 원소 기반이라 원소를 추가하면 해시가 바뀌어 같은 사고가 납니다."

Q53. String, StringBuilder, StringBuffer 의 차이는?

A. "String불변이라 연결할 때마다 새 객체가 만들어집니다. 반복문에서 문자열을 이어 붙이면 그만큼 객체가 생겨서 비쌉니다. StringBuilder가변이고 동기화가 없어 빠르며, StringBuffer메서드에 동기화가 걸려 스레드 안전하지만 그만큼 느립니다. 단일 스레드에서는 StringBuilder 를 쓰는 것이 기본입니다."

↳ "String 이 불변인 이유는요?" → "문자열 상수 풀에서 공유할 수 있고, 해시를 캐싱할 수 있고, 여러 스레드가 안전하게 나눠 쓸 수 있기 때문입니다. 보안상으로도 파일 경로 같은 값이 검사 후에 바뀌는 걸 막습니다."

↳ ""a" + "b" 도 객체가 새로 생기나요?" → "컴파일 시점 상수 표현식은 컴파일러가 합칩니다. 문제가 되는 건 변수를 반복문 안에서 이어 붙일 때입니다."

Q54. 오버로딩과 오버라이딩의 차이는?

A. "**오버로딩**은 같은 이름에 다른 파라미터로 여러 메서드를 두는 것이고, 어떤 걸 호출할지는 컴파일 시점에 정적 타입으로 결정됩니다. **오버라이딩**은 상위 클래스의 메서드를 재정의하는 것이고, 실행 시점에 실제 객체 타입으로 결정됩니다. 다형성이 여기서 나옵니다."

↳ "List.remove(1)remove(Integer.valueOf(1)) 은 다른가요?" → "다릅니다. remove(int)인덱스, remove(Object)을 지웁니다. remove(1) 은 정확히 일치하는 int 오버로드가 있으므로 인덱스 1번을 지웁니다. 값으로 지우려면 remove(Integer.valueOf(1)) 로 명시해야 합니다. 실제로 사고가 자주 나는 지점입니다."

Q55. 인터페이스와 추상 클래스는 어떻게 다르고 언제 무엇을 쓰나요?

A. "인터페이스는 규약이고 다중 구현이 되며, 추상 클래스는 공통 구현을 물려주는 것이고 단일 상속만 됩니다. 판단 기준은 관계입니다. '이것을 할 수 있다' 는 역할이면 인터페이스, '이것의 한 종류다' 는 계층이면 추상 클래스로 둡니다. 저는 기본적으로 인터페이스로 두고, 중복 구현이 실제로 쌓였을 때만 추상 클래스를 꺼냅니다."

↳ "자바 8 부터 인터페이스에 default 메서드가 생겼는데, 그럼 차이가 없어진 건가요?" → "구현을 가질 수 있게 된 건 맞지만 상태(필드)를 가질 수 없다는 차이가 남습니다. 그리고 여전히 다중 구현이 되니 역할 표현에는 인터페이스가 맞습니다."

Q56. Checked 예외와 Unchecked 예외의 차이는?

A. "Checked 는 컴파일러가 처리를 강제합니다. IOException 처럼 호출자가 복구를 시도할 수 있는 상황을 나타냅니다. Unchecked 는 RuntimeException 계열로 강제되지 않고, 보통 프로그래밍 오류(잘못된 인자, null 참조)를 나타냅니다. 스프링은 대부분 런타임 예외로 감싸는데, @Transactional기본 롤백 대상이 RuntimeExceptionError 이기 때문에 이 선택이 트랜잭션 동작과 맞물립니다."

↳ "그럼 Checked 예외가 나면 롤백이 안 되나요?" → "기본 설정에서는 커밋됩니다. 필요하면 @Transactional(rollbackFor = Exception.class) 로 명시해야 합니다. 모르면 사고가 나는 부분입니다."

Q57. GC 를 설명해주세요. Stop-the-World 가 무엇인가요?

A. "더 이상 도달할 수 없는 객체를 자동으로 회수하는 기능입니다. GC 루트(스택 지역변수·정적 필드 등)에서 참조를 따라가 살아 있는 것을 표시하고, 나머지를 회수합니다. 객체 대부분이 금방 죽는다는 특성 때문에 Young 과 Old 세대로 나눠 관리합니다. Stop-the-World 는 이 작업을 위해 애플리케이션 스레드를 멈추는 구간이고, 이게 길어지면 응답 지연으로 나타납니다."

↳ "그럼 메모리 누수는 안 생기나요?" → "생깁니다. GC 는 참조가 남아 있으면 회수하지 않습니다. 정적 컬렉션에 계속 담기만 하거나, 캐시에서 제거를 안 하거나, ThreadLocal 을 정리하지 않으면 도달 가능한 채로 쌓여서 결국 OOM 이 납니다."

↳ "System.gc() 를 호출하면 되나요?" → "권고일 뿐 보장이 없고, 오히려 전체 GC 를 유발해 지연을 만들 수 있어서 운영 코드에서는 쓰지 않습니다."

Q58. 접근 제어자를 좁은 순서로 말해보세요.

A. "privatedefault(package-private) → protectedpublic 순으로 넓어집니다. protected 는 같은 패키지와 상속한 자식에서 접근할 수 있습니다. 기본 원칙은 가장 좁게 시작해서 필요할 때만 넓히는 것입니다."

Q59. 불변 객체를 왜 쓰나요?

A. "생성 시점에 검증을 끝내면 그 뒤로 상태가 깨질 수 없기 때문입니다. 여러 스레드가 공유해도 동기화가 필요 없고, 앞서 말한 것처럼 해시 컬렉션의 키로 안전합니다. 만드는 방법은 필드를 final 로 두고, setter 를 없애고, 컬렉션이나 가변 객체는 방어적 복사로 넣고 꺼내는 것입니다."

↳ "final 만 붙이면 불변인가요?" → "아닙니다. final List재할당만 막고 내용 변경은 막지 못합니다. List.copyOfCollections.unmodifiableList 가 필요합니다."

Q60. IoC 와 DI 를 설명해주세요. 왜 필요한가요?

A. "IoC 는 객체 생성과 연결의 제어권을 컨테이너가 갖는 것이고, DI 는 필요한 의존을 밖에서 주입하는 것입니다. 필요한 이유는 의존 대상을 갈아 끼울 수 있게 되기 때문입니다. 제 프로젝트에서 로또 번호 생성기를 인터페이스로 두고 주입받게 했더니, 테스트에서는 고정된 번호를 주는 구현을 넣어 결과를 검증할 수 있었습니다. new 로 직접 만들면 그게 불가능합니다."

왜 그렇게 답하는가 — 정의만 말하면 다 똑같은 답이 된다. "테스트에서 바꿔 끼웠다" 는 자기 경험이 붙으면 이해한 사람의 답이 된다.

Q61. 생성자 주입을 왜 권장하나요?

A. "세 가지입니다. ① 필수 의존이 없으면 객체를 만들 수 없게 되어 누락이 컴파일·기동 시점에 드러납니다. ② 필드를 final 로 둘 수 있어 불변이 됩니다. ③ 순환 참조가 기동 시점에 실패로 드러납니다. 필드 주입new 로 테스트 객체를 만들 때 의존을 넣을 방법이 없어서 테스트가 불편해집니다."

↳ "필드 주입은 왜 순환 참조를 늦게 발견하나요?" → "필드 주입은 객체를 먼저 만들고 나중에 넣기 때문에 서로를 참조해도 일단 생성은 됩니다. 생성자 주입은 서로가 상대를 먼저 필요로 하니 만들 수 없어서 즉시 드러납니다."

Q62. 빈 스코프에는 무엇이 있고 기본은 무엇인가요? 싱글톤이 아닐 때 생기는 문제는?

A. "기본은 **싱글톤**이고, 그 외에 prototype, request, session 등이 있습니다. 싱글톤은 하나의 인스턴스를 모든 요청이 공유하기 때문에, 빈에 상태를 두면 요청 사이에 값이 섞입니다. 그래서 스프링 빈은 상태 없이 두고, 요청별 데이터는 파라미터나 지역 변수로 다룹니다."

↳ "그럼 싱글톤 빈에 @Autowired 로 request 스코프 빈을 넣으면요?" → "생성 시점에는 요청이 없어서 실패합니다. 프록시로 감싸서 실제 사용 시점에 현재 요청의 것을 찾아가게 해야 합니다."

↳ "싱글톤이면 멀티스레드 문제는 없나요?" → "빈이 상태를 안 가지면 문제 없습니다. 필드에 값을 담는 순간 동시성 문제가 생깁니다."

Q63. Filter 와 Interceptor 의 차이는?

A. "Filter 는 서블릿 컨테이너 레벨이라 스프링 컨텍스트 밖에서 동작하고, DispatcherServlet 에 도달하기 전에 걸립니다. Interceptor 는 스프링 MVC 안에서 핸들러 실행 전후로 동작하고, 어떤 핸들러가 선택됐는지 알 수 있습니다. 그래서 인증처럼 요청 전체를 막는 건 Filter(Spring Security 도 필터 기반), 컨트롤러 정보가 필요한 로깅·권한 검사는 Interceptor 로 둡니다."

Q64. AOP 가 무엇이고 어디에 쓰나요?

A. "트랜잭션·로깅·보안처럼 여러 곳에 흩어지는 공통 관심사를 분리하는 기법입니다. 스프링은 이걸 프록시로 구현합니다. 빈을 감싼 프록시가 먼저 호출을 받아 부가 기능을 실행하고 실제 대상을 호출합니다. 그래서 같은 클래스 안에서 메서드를 직접 호출하면 프록시를 거치지 않아 AOP 가 안 걸립니다."

↳ "그게 실제로 어떤 사고를 만드나요?" → "@Transactional 이 걸린 메서드를 같은 클래스의 다른 메서드에서 this.method() 로 부르면 트랜잭션이 시작되지 않습니다. private 메서드에 붙여도 프록시가 가로챌 수 없어 무시됩니다. 해결은 클래스를 분리하거나 자기 자신을 주입받아 호출하는 것입니다."

Q65. 영속성 컨텍스트와 더티 체킹을 설명해주세요.

A. "영속성 컨텍스트는 **엔티티를 관리하는 1차 캐시**입니다. 같은 트랜잭션에서 같은 ID 를 조회하면 동일한 객체가 반환되고(동일성 보장), 변경은 즉시 쿼리로 나가지 않고 모아 뒀다가 커밋 시점에 나갑니다(쓰기 지연). 더티 체킹은 조회 시점의 스냅샷과 커밋 시점의 값을 비교해 바뀐 필드에 대해 UPDATE 를 만드는 것입니다. 그래서 setter 만 호출해도 반영됩니다."

↳ "save() 를 호출하지 않아도 저장이 되던데 왜죠?" → "영속 상태 엔티티는 더티 체킹 대상이라 트랜잭션 커밋 시 자동으로 반영됩니다. 반대로 트랜잭션이 아니거나 준영속 상태면 반영되지 않습니다. 이 차이를 모르면 '왜 어떤 건 저장되고 어떤 건 안 되나' 로 헤매게 됩니다."

↳ "1차 캐시가 있으면 조회 성능이 좋아지나요?" → "트랜잭션 범위 안에서만 유효해서 성능 목적으로 기대하기는 어렵습니다. 동일성 보장이 본래 목적입니다."

Q66. @Transactional 의 전파 속성 기본값은 무엇인가요?

A. "REQUIRED 입니다. 진행 중인 트랜잭션이 있으면 거기에 참여하고, 없으면 새로 시작합니다. 기본값이 이것인 이유는 서비스 메서드가 서로를 호출할 때 하나의 작업 단위로 묶이는 게 자연스럽기 때문입니다. REQUIRES_NEW 는 항상 새 트랜잭션을 만들어서, 로그 기록처럼 본 작업이 롤백돼도 남아야 하는 것에 씁니다."

↳ "참여한 트랜잭션 안에서 예외를 잡아서 처리하면 커밋되나요?" → "아닙니다. 참여 중인 트랜잭션에서 런타임 예외가 나면 롤백 표시가 남아서, 바깥에서 예외를 삼켜도 커밋 시점에 UnexpectedRollbackException 이 납니다. 신입이 자주 겪는 함정입니다."

Q67. JPA 를 왜 쓰나요? 단점은?

A. "객체와 테이블의 매핑을 자동화해서 비즈니스 로직이 SQL 에 끌려가지 않게 합니다. 반복적인 CRUD 쿼리를 직접 안 써도 되고, DB 종류가 바뀌어도 영향이 적습니다. 단점은 생성되는 쿼리를 모르면 성능 문제가 숨는다는 것입니다. N+1 이나 불필요한 즉시 로딩이 대표적이고, 통계성 복잡 쿼리는 오히려 네이티브 SQL 이나 QueryDSL 이 낫습니다."

Q68. 테스트 코드를 짜본 적 있나요? 왜 짜나요?

A. "네, 대여·반납 로직에 단위 테스트를 썼습니다. 이유는 두 가지입니다. ① 회귀를 막는 것 — 기능을 추가하다 기존 규칙을 깨면 즉시 알 수 있습니다. ② 리팩터링을 겁내지 않게 되는 것 — 구조를 바꿔도 동작이 같다는 보장이 생깁니다. 부수적으로 테스트가 잘 안 써지는 코드는 결합이 강한 코드라서, 설계 진단 도구로도 썼습니다."

↳ "무엇을 테스트하고 무엇을 안 하나요?" → "규칙과 분기가 있는 도메인 로직은 반드시 하고, 단순 위임이나 getter 는 하지 않습니다. 커버리지 숫자를 목표로 삼지 않았습니다."

↳ "@Transactional 을 테스트에 붙이면 왜 편한가요? 함정은요?" → "테스트가 끝나면 자동 롤백돼서 DB 가 깨끝하게 유지됩니다. 다만 실제 커밋 시점에만 드러나는 문제(제약 위반, 지연 로딩 예외)를 놓칠 수 있어서 중요한 흐름은 커밋되는 테스트로도 확인해야 합니다."


F. API · 인증

Q69. REST 를 설명해주세요. 좋은 API 설계 원칙은?

A. "자원을 URI 로 식별하고, 행위는 HTTP 메서드로 표현하는 방식입니다. GET /orders/1, DELETE /orders/1 처럼요. 원칙은 ① URI 에 동사를 넣지 않고 명사 복수형을 쓰고, ② 결과는 상태 코드로 알리고, ③ 계층은 /members/1/orders 처럼 소유 관계로 표현하고, ④ 이름 규칙을 일관되게 유지하는 것입니다."

✕ 감점POST /getOrderList 같은 예를 좋은 설계로 드는 것.

Q70. HTTP 메서드를 설명해주세요. GET 과 POST 의 차이는?

A. "GET 은 조회로 멱등하고 캐시 가능하며, POST 는 생성으로 멱등하지 않습니다. PUT 은 전체 교체로 멱등, PATCH 는 부분 수정, DELETE 는 삭제로 멱등입니다. GET 과 POST 의 실질적 차이는 부수 효과의 유무입니다. GET 은 서버 상태를 바꾸지 않는다는 약속이 있어서 브라우저·프록시가 마음대로 캐시하고 재시도합니다. 그래서 상태를 바꾸는 일을 GET 으로 만들면 위험합니다."

✕ 감점 — "GET 은 주소창에 보이고 POST 는 안 보이니 POST 가 안전하다". 암호화와 무관하다.

Q71. 401 과 403, 400 과 422 는 어떤 기준으로 구분하나요?

A. "401 은 인증 실패 — 누구인지 모르거나 토큰이 유효하지 않은 경우입니다. 403 은 인증은 됐지만 권한이 없는 경우입니다. '로그인하면 될 문제' 면 401, '로그인해도 안 될 문제' 면 403 으로 나눕니다. 400 은 요청 형식 자체가 잘못된 경우(JSON 파싱 실패, 필수 필드 누락), 422 는 형식은 맞지만 의미상 처리할 수 없는 경우(날짜 범위가 역전, 존재하지 않는 상태 전이)입니다."

Q72. 모든 응답을 200 으로 주고 본문에 에러 코드를 담으면 무엇이 문제인가요?

A. "클라이언트와 중간 장비, 모니터링이 모두 성공으로 오해합니다. 프록시·CDN 이 에러 응답을 캐시할 수 있고, 라이브러리의 자동 재시도 판단이 망가지고, APM 의 에러율 지표가 항상 0으로 나와서 장애를 알림으로 잡을 수 없게 됩니다."

↳ "그럼 에러 응답은 어떻게 설계하나요?" → "상태 코드로 분류를 주고, 본문에는 코드·메시지·필드별 사유를 일관된 형식으로 담습니다. 검증 오류가 여러 필드에서 나면 첫 번째만 반환하지 말고 배열로 전부 줘야 사용자가 한 번에 고칠 수 있습니다."

Q73. 결제 API 에서 타임아웃 후 재시도로 이중 결제가 났습니다. 어떻게 막나요?

A. "요청마다 클라이언트가 Idempotency-Key 를 만들어 보내고, 서버는 그 키와 결과를 저장해서 같은 키로 다시 오면 새로 처리하지 않고 저장된 결과를 반환하게 합니다. 그리고 최종 안전망으로 DB 에 유니크 제약을 둡니다. 애플리케이션 레벨 검사만으로는 서버가 여러 대일 때 동시 요청을 완벽히 막을 수 없어서, 정합성의 최종 판정은 DB 제약에 맡깁니다."

↳ "타임아웃이 났을 때 실제로 처리됐는지 모르면요?" → "조회 API 로 상태를 먼저 확인하고 결정합니다. 그래서 결제 같은 흐름은 처음부터 '생성 후 상태 조회' 가 가능하게 설계해야 합니다."

Q74. JWT 의 구조를 설명하고, 왜 썼는지 말해주세요.

A. "헤더·페이로드·서명 세 부분이 점으로 이어진 형태입니다. 헤더에 알고리즘, 페이로드에 사용자 식별자와 만료 시각, 서명은 앞의 둘을 비밀키로 서명한 값입니다. 쓴 이유는 서버가 세션을 들고 있지 않아 인스턴스가 늘어나도 상태 공유가 필요 없기 때문입니다. 중요한 건 페이로드는 암호화가 아니라 base64url 인코딩이라는 점입니다. 누구나 열어볼 수 있어서 민감정보를 넣으면 안 되고, 서명은 위조를 막을 뿐입니다."

왜 이 답이 좋은가 — "인코딩일 뿐 암호화가 아니다" 를 스스로 말하는 신입은 드물다. 이 한 문장이 보안 감각의 증거가 된다.

↳ "그럼 JWT 의 단점은요?" → "만료 전에 취소하기 어렵다는 점입니다. 서버가 상태를 안 갖는 게 장점인데 취소하려면 상태를 가져야 해서 모순이 생깁니다. 그래서 만료를 짧게 두고 리프레시 토큰으로 갱신하는 구조를 씁니다."

Q75. 세션 인증과 JWT 중 무엇을 고르겠습니까?

A. "즉시 무효화와 서버 통제가 중요하면 세션, 무상태 확장이 중요하면 JWT 로 고릅니다. 서버가 몇 대 안 되고 로그아웃·강제 탈퇴가 즉시 반영돼야 하는 서비스라면 Redis 세션 저장소를 두는 편이 단순하고 안전합니다. 반대로 인스턴스가 자주 늘고 줄거나 여러 서비스가 같은 토큰을 검증해야 하면 JWT 가 맞습니다. 저는 요구사항에서 선택이 나와야 한다고 생각합니다."

✕ 감점 — "요즘은 JWT 를 많이 쓰니까 JWT" 라고 답하는 것.

Q76. Refresh Token 은 어디에 저장하나요?

A. "HttpOnly·Secure 쿠키에 두는 것이 기본입니다. 로컬스토리지는 자바스크립트로 읽을 수 있어서 XSS 한 번에 탈취됩니다. HttpOnly 면 스크립트가 접근할 수 없고, Secure 면 HTTPS 로만 전송됩니다. 쿠키는 자동 전송되니 CSRF 대비로 SameSite 설정을 함께 둡니다. 그리고 서버에 저장해 재사용 감지와 회전(rotation) 을 하면 탈취 피해를 줄일 수 있습니다."

Q77. CORS 가 무엇이고 왜 생기나요?

A. "브라우저의 동일 출처 정책 때문에, 다른 출처로 보낸 요청의 응답을 스크립트가 읽지 못하게 막는 것입니다. 서버가 Access-Control-Allow-Origin 같은 헤더로 허용을 밝혀야 풀립니다. 메서드나 헤더가 단순하지 않으면 브라우저가 먼저 OPTIONS 로 예비 요청(preflight) 을 보냅니다. 중요한 건 차단하는 주체가 브라우저라는 점입니다. 서버는 이미 요청을 받아 처리했고, 응답을 못 읽게 막힌 것입니다."

✕ 감점 — "프론트에서 CORS 를 끄면 됩니다". 개발용 우회일 뿐 해결이 아니다.

Q78. API 문서화에 Mock 대신 Swagger 를 쓴 이유는?

A. "코드에서 문서가 생성되니 스펙과 구현이 어긋나지 않기 때문입니다. 문서를 따로 관리하면 API 가 바뀔 때마다 갱신을 잊게 되는데, 저희는 실제로 그 문제로 프론트와 계속 어긋났습니다. 게다가 Swagger UI 에서 바로 요청을 보내 볼 수 있어서 프론트가 서버 없이도 스펙을 확인할 수 있었습니다."

↳ "그럼 Swagger 의 단점은요?" → "애너테이션이 컨트롤러에 늘어나 코드가 지저분해집니다. 그리고 문서가 자동 생성돼도 정확한 설명은 사람이 써야 합니다. 그래서 REST Docs 처럼 테스트 기반으로 만드는 방식도 함께 검토합니다."


G. 배포 · 협업

Q79. 도커를 설명하고 왜 쓰는지 말해주세요. 가상머신과 차이는?

A. "애플리케이션과 실행 환경을 이미지로 묶어 어디서나 같게 실행하게 해주는 도구입니다. 가상머신은 게스트 OS 를 따로 띄우지만, 컨테이너는 호스트 커널을 공유하고 namespace 로 격리하고 cgroup 으로 자원을 제한합니다. 결국 **호스트에서 보면 그냥 프로세스**입니다. 그래서 시작이 초 단위로 빠르고 이미지가 가볍습니다. 제가 쓴 이유는 '제 컴퓨터에서는 되는데' 문제를 없애는 것이었습니다."

↳ "그럼 컨테이너가 VM 보다 항상 좋은가요?" → "아닙니다. 커널을 공유하니 격리 수준은 VM 보다 약합니다. 다른 커널이 필요하거나 강한 보안 격리가 필요하면 VM 이 맞습니다."

Q80. 도커가 있는데 쿠버네티스는 왜 필요한가요?

A. "도커는 컨테이너 하나를 실행하는 도구고, 쿠버네티스는 여러 노드에 걸쳐 컨테이너를 배치하고 관리하는 도구입니다. 컨테이너가 수십 개가 되면 어느 서버에 띄울지, 죽으면 누가 다시 띄울지, 트래픽에 따라 몇 개로 늘릴지, 새 버전을 어떻게 무중단으로 바꿀지를 사람이 할 수 없습니다. 쿠버네티스는 원하는 상태를 선언하면 실제 상태를 거기에 맞추는 방식으로 그걸 자동화합니다."

✕ 감점 — "쿠버네티스는 도커의 상위 버전입니다". 역할이 다른 도구다.

Q81. CI/CD 를 설명해주세요.

A. "CI 는 커밋마다 자동으로 빌드와 테스트를 돌려 통합 문제를 빨리 찾는 것이고, CD 는 통과한 산출물을 자동으로 배포하는 것입니다. 제 프로젝트에서는 GitHub Actions 로 PR 마다 빌드와 테스트를 돌리고, main 에 머지되면 이미지를 만들어 서버에 배포하게 해뒀습니다. 효과는 깨진 코드가 main 에 들어가지 않는 것이었습니다."

↳ "무중단 배포는 어떻게 하나요?" → "롤링은 인스턴스를 조금씩 교체해 무중단이지만 두 버전이 잠시 공존합니다. 블루/그린은 환경을 둘 두고 한 번에 전환해 롤백이 빠르지만 자원이 두 배 듭니다. 카나리는 일부 트래픽만 새 버전으로 보내 지표를 보고 확대합니다."

Q82. 사용한 Git 전략을 설명해주세요. git flow 의 브랜치와 역할은?

A. "main 은 배포되는 코드, develop 은 통합 브랜치, feature/* 는 기능 개발, release/* 는 배포 준비, hotfix/* 는 운영 긴급 수정입니다. 다만 저희는 배포가 잦고 인원이 적어서 GitHub flow 로 단순화했습니다 — main 하나에 feature 브랜치를 따서 PR 로 머지하는 방식입니다. 전략은 배포 주기와 팀 규모에 맞춰 고르는 것이라고 생각합니다."

왜 그렇게 답하는가 — 브랜치 이름을 외워 말하는 것보다 왜 단순화했는지가 판단력을 보여 준다.

Q83. 버전 관리에서 충돌을 겪은 적 있나요? 어떻게 해결했나요?

A. "같은 파일을 둘이 크게 고쳐서 충돌이 컸던 적이 있습니다. 해결은 했지만 근본 원인은 브랜치를 오래 들고 있었던 것이라고 봤습니다. 그래서 그 뒤로는 작업을 작게 쪼개 PR 을 자주 올리고, 작업 전에 main 을 받아 오는 습관을 들였습니다. 파일 단위로 담당을 나눈 것도 도움이 됐습니다."

Q84. 모놀리식과 MSA 를 설명해주세요.

A. "모놀리식은 하나의 배포 단위, MSA 는 기능별로 나눠 독립 배포하는 구조입니다. 나누는 이유는 기술이 아니라 대개 조직입니다. 팀이 많아지면 하나의 배포 단위에서 서로 발목을 잡게 되니까요. 대가는 큽니다 — **분산 트랜잭션, 네트워크 실패 처리, 운영·모니터링 복잡도**가 늘어납니다. 그래서 신입인 제 판단으로는 처음에는 모놀리식으로 시작해 경계를 잘 나눠 두고, 필요해질 때 떼는 편이 맞다고 생각합니다."

왜 그렇게 답하는가 — MSA 를 좋은 것으로만 말하면 반드시 대가를 되묻는다. "먼저 모놀리식" 이라는 판단은 신입이 말해도 설득력이 있다.


H. 인턴 면접에 특히 나오는 것

Q85. 자신 없는 분야에 배정되면 어떻게 하실 건가요?

A. "먼저 무엇을 모르는지 목록으로 만들겠습니다. 그다음 공식 문서와 기존 코드를 읽고, 가장 작은 단위로 만들어 동작을 확인하면서 범위를 넓히겠습니다. 막히면 30분에서 1시간 안에 제가 확인한 것과 시도한 것을 정리해서 질문하겠습니다. 프리코스에서 처음 쓰는 라이브러리를 그렇게 익힌 경험이 있어서, 방법은 있다고 생각합니다."

왜 그렇게 답하는가 — 인턴 면접의 단골이고, 보는 것은 적응력과 태도다. "열심히 배우겠습니다" 로 끝내면 아무 정보가 없다. 방법을 절차로 말해야 한다. 거부 뉘앙스는 곧 탈락이다.

✕ 감점 — "제가 해본 분야가 아니라 자신이 없습니다". 질문의 의도를 거꾸로 받은 답이다.

Q86. 데이터 분석 파트에 배정될 수도 있는데 괜찮나요?

A. "괜찮습니다. 제가 백엔드를 하면서도 결국 데이터를 다루는 일이라고 느꼈습니다. 쿼리로 지표를 뽑아 본 경험이 있고, 로그를 집계해 어느 API 가 느린지 찾아본 적도 있습니다. 분석 쪽 도구는 새로 배워야 하지만, 데이터를 의심하고 검증하는 태도는 지금 하는 일과 이어진다고 생각합니다."

왜 그렇게 답하는가 — 실제로 나온 질문이다. 조직 사정에 대한 유연성을 보는 것이니, 관심 없다고 답하기보다 지금 역량과의 연결 고리를 찾아 말한다.

Q87. 좋은 리더란 어떤 사람이라고 생각하나요?

A. "결정을 미루지 않는 사람이라고 생각합니다. 팀 프로젝트에서 기술 선택을 두고 논의가 길어졌을 때, 정보가 완벽하지 않아도 '이번엔 이걸로 가고 문제가 생기면 바꾸자' 고 정해 준 사람이 있었습니다. 그때 진도가 다시 돌기 시작했습니다. 판단의 근거를 공유하고 책임을 지는 것이 좋은 리더의 조건이라고 봅니다."

왜 그렇게 답하는가 — 정답이 없는 질문이므로 정의 + 경험 + 이유 세 칸을 채운다. 경험이 없으면 추상어만 남아 누구나 하는 답이 된다.

Q88. 짧은 인턴 기간에 무엇을 남기고 싶은가요?

A. "작아도 끝까지 완결된 것 하나를 남기고 싶습니다. 예를 들어 반복되는 수동 작업을 스크립트로 만들거나, 테스트가 없는 모듈에 테스트를 붙이거나, 신규 입사자가 겪는 셋업 과정을 문서로 정리하는 것처럼요. 기간이 짧으니 큰 기능보다 인턴이 끝난 뒤에도 팀에 남는 것이 낫다고 생각합니다."

✕ 감점 — "많이 배우고 싶습니다" 로 끝내기. 받아 갈 것만 말하고 남길 것이 없다.

Q89. Kafka 써봤나요? (경험이 없는 도구를 묻는 질문)

A. 실제 후기에 좋은 모범이 남아 있다. 지원자는 이렇게 답했다 — "트래픽이 있는 데이터를 다뤄본 적이 없어서 써보지는 못했습니다." 면접관도 개인이 경험하기 어려운 영역이라며 공감했다.

여기에 한 문장을 더 붙이면 완성된다. "다만 역할은 이렇게 이해하고 있습니다 — 생산자와 소비자를 분리해서, 소비자가 느려도 생산자가 막히지 않게 해주는 버퍼이고, 파티션 단위로 순서가 보장된다고 알고 있습니다. 기회가 되면 직접 써보고 싶습니다."

① 경험 없음을 인정한다        아는 척은 꼬리질문 두 번이면 반드시 무너진다
② 왜 못 해봤는지 맥락을 준다   게으름이 아니라 환경의 문제였다는 것
③ 아는 만큼은 정확히 말한다    "역할은 이렇게 이해하고 있습니다" 까지
④ 배울 의사를 붙인다          한 문장이면 충분하다

✕ 감점 — 들어본 지식으로 써봤다고 말하는 것. "그럼 파티션은 어떻게 나눴나요" 한 번에 무너지고, 그 뒤의 모든 답변의 신뢰도가 떨어진다.

Q90. 마지막으로 궁금한 점 있나요? (배점 10%)

A. 배점이 있는 항목이다. 연봉·복지만 묻고 끝내면 스스로 점수를 버리는 것이다. 좋은 역질문은 내가 여기서 어떻게 일하고 성장할지를 묻는 것이다.

신입이 처음 6개월 동안 주로 어떤 일을 맡게 되나요?
코드 리뷰는 어떤 방식으로 도나요? 리뷰에서 주로 어떤 점을 보시나요?
테스트와 배포는 어디까지 자동화돼 있나요?
제가 입사 전까지 준비하면 도움이 될 것이 있을까요?
지금 팀에서 가장 큰 기술적 과제는 무엇인가요?

마지막 두 개가 특히 좋다. 배우려는 자세팀의 문제에 대한 관심을 동시에 보여 준다.

✕ 감점 — "없습니다". 관심이 없다는 뜻으로 읽힌다. 최소 두 개는 준비해 간다.


I. 실전 도구·설계 — 공고 우대사항에 자주 있는 것들

Q91. QueryDSL 을 왜 쓰나요?

A. "문자열로 쿼리를 짜면 두 가지가 괴롭습니다. 오타나 타입 오류를 런타임에야 알게 되고, 조건이 동적으로 바뀌는 검색StringBuilder 로 이어 붙이다 보면 WHERE 1=1 AND ... 같은 코드가 됩니다. QueryDSL 은 엔티티에서 생성한 Q 클래스로 쿼리를 자바 코드처럼 조립해서 컴파일 시점에 검증되고, 동적 조건을 깨끝하게 표현할 수 있습니다. 저는 검색 화면의 필터가 늘어나면서 도입했습니다."

왜 그렇게 답하는가 — "타입 안전" 만 말하면 절반이다. 동적 쿼리가 QueryDSL 의 진짜 이유이고, 면접관이 듣고 싶은 것도 그쪽이다.

↳ "Spring Data JPA 의 메서드 이름 쿼리로는 안 되나요?" → "조건이 두세 개면 됩니다. 하지만 조건이 늘어나면 findByNameAndAgeGoeAndCityAndStatus... 처럼 이름이 폭발하고, 선택적 조건 조합만큼 메서드를 만들어야 합니다. 조건 4개면 조합이 16가지라 그 지점에서 QueryDSL 로 넘어가는 게 맞다고 봅니다."

Q92. QueryDSL 의 Q 클래스는 어떻게 만들어지나요?

A. "애너테이션 프로세서가 빌드 시점에 @Entity 를 읽어 QMember 같은 메타모델 클래스를 생성합니다. 그 Q 클래스의 필드로 조건을 쓰기 때문에 member.age.goe(20) 처럼 타입이 맞지 않으면 컴파일 에러가 납니다. 대신 엔티티를 바꾸면 Q 클래스를 다시 생성해야 하고, 그래서 빌드 설정(annotationProcessor)이 필요합니다."

↳ "Spring Boot 3 로 올리면서 QueryDSL 이 깨졌다는 얘기가 있는데 아세요?" → "네, 원인은 javax.persistence 에서 jakarta.persistence 로 바뀐 것입니다. 원조 com.querydsl 은 사실상 유지보수가 멈춰서 Jakarta 대응이 안 됩니다. 그래서 지금은 커뮤니티가 이어받은 OpenFeign 포크 io.github.openfeign.querydsljakarta classifier 로 쓰는 게 사실상 표준입니다. 마이그레이션의 핵심이 groupId 와 classifier 교체였습니다."

왜 이 답이 강한가 — 라이브러리의 유지보수 상태와 포크 사정까지 아는 신입은 드물다. "공식 문서만 본" 것과 "실제로 빌드하다 깨져 본" 것의 차이가 여기서 드러난다.

Q93. 동적 검색 조건은 어떻게 처리했나요?

A. "조건이 없으면 null 을 반환하는 메서드를 만들어 where() 에 나열했습니다. QueryDSLwhere() 안의 null 을 자동으로 무시하기 때문에, 입력이 없는 조건은 그냥 빠집니다.

private BooleanExpression emailEq(String email) {
    return email != null ? member.email.eq(email) : null;
}
private BooleanExpression ageGoe(Integer age) {
    return age != null ? member.age.goe(age) : null;
}

query.selectFrom(member)
     .where(emailEq(email), ageGoe(age))   // null 조건은 AND에서 제외
     .fetch();

BooleanBuilder 로 모으는 방법도 있지만, 조건 메서드를 재사용할 수 있어서 저는 이 방식을 더 선호했습니다."

↳ "조건이 전부 null 이면 어떻게 되나요?" → "WHERE 절이 사라져 전체 조회가 됩니다. 그래서 실제 서비스라면 최소 조건을 강제하거나 페이지 크기 상한을 둬야 합니다. 전체 스캔이 나가는 사고를 막는 부분입니다."

Q94. JPA·QueryDSL·MyBatis·네이티브 SQL 중 무엇을 언제 쓰나요?

A. "저는 이렇게 나눕니다. 단순 CRUD 와 엔티티 중심 로직은 Spring Data JPA, 조건이 동적이거나 복잡한 조회는 QueryDSL, DB 고유 기능이 꼭 필요한 쿼리는 네이티브 SQL 로 그 쿼리만 씁니다. MyBatisSQL 을 직접 통제하고 싶거나 레거시 쿼리가 이미 많은 경우에 강합니다. 하나로 통일하려 하기보다 한 프로젝트에서 섞어 쓰는 게 보통이라고 이해하고 있습니다."

↳ "QueryDSL 의 한계는요?" → "DB 특화 문법은 표현에 한계가 있어서 재귀 CTE 나 JSON 연산 같은 건 결국 네이티브로 빠집니다. 그리고 결국 JPA 위에서 도는 도구라, 영속성 컨텍스트와 페치 전략을 모르면 QueryDSL 을 써도 N+1 은 그대로 납니다."

✕ 감점 — "QueryDSL 이 제일 좋아서 다 QueryDSL 로 씁니다". 도구의 경계를 모른다는 뜻이다.

Q95. 엔티티를 그대로 API 응답으로 반환하면 안 되는 이유는?

A. "세 가지 문제가 있습니다. ① 내부 필드가 그대로 노출됩니다 — 비밀번호나 내부 상태 컬럼까지 나갑니다. ② API 스펙이 엔티티에 묶입니다 — DB 컬럼명을 바꾸면 클라이언트가 깨집니다. ③ 지연 로딩 연관을 직렬화하다 예외가 나거나, 양방향 연관에서 무한 순환이 생깁니다. 그래서 응답은 필요한 필드만 담은 DTO 로 변환해서 내보냅니다."

왜 그렇게 답하는가 — 신입 코드 리뷰에서 가장 자주 지적되는 항목이라, 면접에서도 자주 확인한다. 세 이유를 구분해 말하면 실제로 겪어 본 사람으로 읽힌다.

↳ "그럼 DTO 변환은 어디서 하나요?" → "저는 서비스 계층에서 DTO 로 변환해 컨트롤러가 엔티티를 아예 모르게 뒀습니다. QueryDSL 을 쓸 때는 Projections조회 단계에서 바로 DTO 로 받으면 필요한 컬럼만 select 되어 더 낫습니다."

Q96. 요청 값 검증은 어디서 하나요?

A. "두 곳으로 나눕니다. 형식 검증은 컨트롤러 입구에서 Bean Validation 으로 처리합니다 — DTO 필드에 @NotBlank, @Email, @Min 을 붙이고 @Valid 로 트리거합니다. 규칙 검증은 도메인이나 서비스에서 합니다. '재고보다 많이 주문할 수 없다' 같은 건 값의 형식 문제가 아니라 도메인 규칙이니까요. 형식 검증을 도메인까지 끌고 가면 도메인이 지저분해지고, 반대로 도메인 규칙을 컨트롤러에 두면 재사용이 안 됩니다."

↳ "@Valid@Validated 는 다른가요?" → "다릅니다. @Valid@RequestBody 같은 객체 파라미터에 붙어 실패 시 MethodArgumentNotValidException 이 납니다. @Validated 는 클래스에 붙여 @RequestParam·@PathVariable 같은 단일 값 검증이나 서비스 계층 검증에 쓰이고, 실패 시 ConstraintViolationException 이 납니다. 예외 타입이 달라서 전역 예외 처리에서 둘 다 잡아야 합니다."

Q97. 예외는 어떻게 처리했나요?

A. "@RestControllerAdvice 에 전역 예외 핸들러를 두고, 비즈니스 예외는 직접 만든 예외 계층으로 던졌습니다. 컨트롤러마다 try-catch 를 쓰면 응답 형식이 제각각이 되니까요. 핸들러에서 예외 종류를 상태 코드로 매핑하고, 본문은 { code, message, errors[] } 처럼 일관된 형식으로 만들었습니다. 검증 실패는 필드별 사유를 배열로 담아 한 번에 고칠 수 있게 했습니다."

↳ "예외 메시지를 그대로 클라이언트에 보내도 되나요?" → "안 됩니다. 스택트레이스나 DB 오류 메시지가 나가면 내부 구조가 노출됩니다. 클라이언트에는 정제된 메시지를 주고, 원본은 서버 로그에 남깁니다."

↳ "500 이 나면요?" → "예상하지 못한 예외이니 로그에 스택트레이스를 남기고 알림으로 연결해야 합니다. 400 대는 사용자 입력 문제라 로그 레벨을 낮게 둡니다."

Q98. 데이터가 100만 건인데 페이징이 뒤로 갈수록 느려집니다. 왜 그런가요?

A. "LIMIT 20 OFFSET 100000건너뛸 10만 건을 실제로 읽고 버리기 때문입니다. 페이지가 뒤로 갈수록 읽고 버리는 양이 늘어나서 선형으로 느려집니다. 해결은 OFFSET 대신 마지막으로 본 값을 기준으로 잘라내는 것입니다.

-- 느림: 뒤로 갈수록 버리는 행이 늘어난다
SELECT * FROM orders ORDER BY id DESC LIMIT 20 OFFSET 100000;

-- 커서 방식: 인덱스로 바로 진입해 20건만 읽는다
SELECT * FROM orders WHERE id < :lastSeenId ORDER BY id DESC LIMIT 20;

정렬 기준이 인덱스를 타면 어느 페이지든 비용이 같습니다."

↳ "그럼 왜 다들 OFFSET 을 쓰나요?" → "임의 페이지로 바로 갈 수 있기 때문입니다. '5페이지로 이동' 같은 UI 는 커서 방식으로는 못 만듭니다. 그래서 번호 페이징이 필요한 관리자 화면은 OFFSET, 무한 스크롤은 커서로 나눕니다."

↳ "전체 건수를 세는 쿼리도 느린데요?" → "COUNT(*) 도 조건에 따라 전체를 세게 됩니다. 정확한 총 개수가 꼭 필요하지 않다면 '다음 페이지가 있는지' 만 확인하도록 한 건 더 조회해서 판단하는 방법을 씁니다."

Q99. 커넥션 풀이 무엇이고 크기는 어떻게 정하나요?

A. "DB 연결은 만드는 비용이 커서, 미리 만들어 두고 빌려 쓰고 반납하는 것이 커넥션 풀입니다. Spring Boot 의 기본은 HikariCP 이고 기본 최대 크기는 10 입니다. 크기는 크게 잡을수록 좋은 게 아닙니다. DB 가 동시에 처리할 수 있는 양을 넘기면 DB 쪽에서 경쟁만 심해져 전체가 느려집니다. 보통 코어 수를 기준으로 시작해서 실제 지표를 보고 조정합니다."

↳ "커넥션이 부족하면 어떤 증상이 나오나요?" → "Connection is not available 타임아웃이 나고, 그 전에 응답 지연이 먼저 늘어납니다. 그런데 진짜 원인은 풀 크기가 아니라 커넥션을 오래 쥐고 있는 코드인 경우가 많습니다. 트랜잭션 안에서 외부 API 를 호출하는 게 대표적입니다."

왜 이 답이 좋은가 — 풀 크기를 올려 해결하려는 게 아니라 점유 시간을 원인으로 지목하는 것이 실무 감각이다.

Q100. 환경별 설정과 비밀값은 어떻게 관리했나요?

A. "프로파일로 분리했습니다. application.yml 에 공통 설정을 두고 application-local.yml, application-prod.yml 로 나눠 실행 시 프로파일을 지정했습니다. DB 비밀번호나 토큰 키 같은 비밀값은 파일에 넣지 않고 환경 변수로 주입했습니다. 저장소에 커밋되면 되돌릴 수 없으니까요."

↳ "실수로 커밋했다면 어떻게 하나요?" → "히스토리에 남으니 지우는 것으로는 부족하고, 그 값을 먼저 폐기하고 재발급해야 합니다. 이후 커밋에서 삭제하는 건 그다음입니다."


답변의 공통 규칙 — 신입 기준

① 모르면 모른다고 한다        아는 척은 꼬리질문 두 번이면 무너진다
② 아는 만큼은 정확히          "정확히는 모르지만 이 정도로 이해하고 있습니다"
③ 근거를 함께               "왜 그렇게 했나" 가 거의 모든 질문의 본질이다
④ 대가를 말한다              장점만 말하면 깊이가 없다고 읽힌다
⑤ 내 프로젝트로 연결          추상적 개념도 내가 만든 것에 붙이면 설명이 산다
⑥ 갈고리를 남긴다            다음 질문을 내가 준비한 영역으로 유도한다

흔한 감점 패턴

외운 정의만 말하고 멈춤 — "언제 쓰냐"가 없으면 암기로 읽힌다.

장점만 나열JWT·MSA·NoSQL·인덱스를 좋다고만 말하면 반드시 대가를 되묻는다.

써보지 않은 것을 써봤다고 함 — 꼬리질문 두 번에서 드러나고, 이후 모든 답의 신뢰도가 떨어진다.

회사 조사 안 함 — 앱을 써봤냐는 질문에 답을 못 하면 지원 동기 전체가 무너진다.

팀 갈등을 남 탓으로 — 협업 질문의 실제 평가 대상은 태도다.

synchronized 로 동시성을 해결했다고 답함 — 서버 한 대에서만 유효하다는 걸 모른다는 신호다.

역질문 없음 — 배점이 있는 항목을 스스로 버리는 것이다.


한눈에 정리

  • 신입·인턴 면접의 무게는 포트폴리오와 인성에 있다. 한 인턴 면접은 포트폴리오 60% · 과제 30% · 역질문 10% 였다
  • 가장 자주 무너지는 질문은 CS 가 아니라 "저희 서비스 써보셨나요"
  • 기술 질문의 형식은 거의 항상 정의 → 언제 쓰나 → 대가다. 정의에서 멈추면 암기로 읽힌다
  • DB 가 가장 깊게 파는 영역 — 인덱스 미사용 패턴·격리 수준 기본값·N+1·락은 꼬리질문까지 준비한다
  • Java 는 equals/hashCode 세트, Spring 은 생성자 주입·싱글톤 상태·AOP 프록시 함정이 단골이다
  • 인증401 vs 403, JWT 페이로드는 암호화가 아니다, 즉시 무효화가 어렵다를 함께 말한다
  • 경험 없는 도구는 인정 + 맥락 + 아는 만큼 + 배울 의사가 정답이다
  • 역질문은 배점이다 — 일·리뷰·자동화·팀의 과제를 묻는다

함께 볼 것 — 개념 깊이는 01~12편 노트, 공고 기반 경력직 질문은 13편, 채용 채널과 자료는 15편.

출처 — 질문은 실제 신입·인턴 면접 후기에서 수집하고, 모범답안은 이 저장소의 01~12편 노트와 1차 자료(JLS·Spring/Hibernate 공식 문서·MySQL·PostgreSQL 매뉴얼·RFC)로 교차 검증해 집필했다 · 신입 백엔드 면접 질문 Ver.2.0.7 — 실제 받은 질문 90여개 · 비전공자의 백엔드 개발자 면접 후기 · 웹 개발 인턴 합격 수기 — 면접 구성·배점 · 백엔드 개발자 면접·학습 내용 정리 · anjeongkyun/backend-interview-questions (2026-08 갱신) · ksundong/backend-interview-question · gyoogle/tech-interview-for-developer

후기 원문의 스택이 Node·Nest 인 항목은 질문의 얼개만 남기고 Java·Spring 기준으로 옮겼다. 회사명이 특정되는 질문은 옮기지 않았다.

채용공고 실전 면접 질문 — 90문항·모범답안(기출·1차검증)신입 채용 채널과 자료 지도 — 공고·GitHub·스터디