
음식 주문 서비스를 완성한 신입 개발자와 모의면접을 진행한 적이 있습니다. 지원자는 회원가입, 메뉴 조회, 장바구니, 주문 기능을 Spring으로 구현했다고 설명했습니다. 준비한 첫 답변은 자연스러웠지만 주문 요청이 들어온 뒤 어떤 데이터가 어떤 순서로 저장되는지 묻자 설명이 끊겼습니다. 주문 정보는 저장됐는데 재고 차감이 실패하면 어떻게 되는지, 존재하지 않는 메뉴를 요청하면 어떤 응답을 반환하는지에 대해서도 명확히 답하지 못했습니다. 기능은 구현했지만 정상적으로 실행된 결과만 기억하고 있었기 때문입니다.
면접 이후 프로젝트를 다시 열어 주문 API의 요청값과 응답 구조, 주문과 주문상품 테이블의 관계, 재고 부족 시 처리 순서를 확인했습니다. 주문 저장과 재고 변경을 하나의 트랜잭션으로 묶고, 잘못된 메뉴 ID와 재고 부족 상황의 오류 응답을 분리했으며 같은 조건을 만들어 재검증했습니다. 이후 답변은 주문 기능을 만들었다는 문장에서 요청 검증, 데이터 일관성, 실패 상황 처리까지 설명하는 구조로 바뀌었습니다. 백엔드 프로젝트는 기능 목록을 외우는 방식으로 준비해서는 면접 답변이 깊어지기 어렵습니다. API설계와 DB구조, 오류처리에 담긴 판단을 복기해야 구현 경험이 기술면접의 근거가 됩니다.
API설계는 요청 목적과 검증 기준이 답변에 보여야 합니다
- 엔드포인트 목록보다 사용자의 행동을 먼저 설명해야 합니다
백엔드 프로젝트를 소개할 때 회원 API, 게시글 API, 주문 API를 구현했다고 나열하는 경우가 많습니다. 그러나 면접관은 엔드포인트의 개수보다 어떤 사용자의 행동을 처리하기 위해 요청과 응답을 구성했는지 확인하려 합니다. 기능의 목적을 먼저 설명해야 세부 설계도 자연스럽게 이어집니다.
스터디룸 예약 서비스를 만든 지원자는 예약 등록과 조회 API를 개발했다고만 준비했습니다. 코드를 다시 확인해 보니 사용자가 날짜와 시간을 선택하면 예약 가능한지 확인하고, 기존 예약과 겹치지 않을 때 데이터를 저장하는 흐름이 있었습니다. 단순한 등록 기능이 아니라 중복 시간을 검사해야 하는 업무 규칙이 포함돼 있었습니다.
- 기능만 말한 답변: 예약 등록과 예약 내역 조회 API를 구현했습니다.
- 요청 흐름이 보이는 답변: 사용자가 스터디룸과 이용 시간을 선택하면 기존 예약과 겹치는지 확인한 뒤 저장하도록 구성했습니다. 종료 시간이 시작 시간보다 빠르거나 이미 예약된 시간과 겹치는 요청은 저장하지 않고 원인을 구분해 응답했습니다.
두 번째 답변은 API 이름보다 사용자의 요청, 서버가 확인한 조건, 처리 결과를 보여줍니다. 이 흐름을 먼저 정리하면 HTTP 메서드와 URL, 요청 데이터에 관한 후속 질문에도 답하기 쉬워집니다.
- 요청값은 형식과 업무 규칙을 나누어 검증해야 합니다
입력값 검증을 했다는 설명만으로는 어떤 값을 어디에서 확인했는지 알기 어렵습니다. 이메일 형식과 빈 값처럼 요청 자체의 형식을 검사하는 항목이 있고, 재고 수량과 예약 가능 여부처럼 데이터와 업무 규칙을 확인해야 하는 항목도 있습니다.
온라인 강의 신청 기능을 예로 들면 사용자 ID와 강의 ID가 전달됐는지 먼저 확인할 수 있습니다. 이후 실제 사용자가 존재하는지, 강의가 모집 중인지, 이미 신청한 강의인지, 정원이 남아 있는지를 데이터베이스와 연결해 검사해야 합니다. 모든 실패를 잘못된 요청 하나로 처리하면 클라이언트는 무엇을 수정해야 하는지 판단하기 어렵습니다.
- 형식 검증은 필수값, 문자열 길이, 숫자 범위와 날짜 형식처럼 요청 데이터 자체에서 확인할 수 있습니다. 이 단계에서 잘못된 값이 내부 로직까지 들어가지 않도록 처리합니다.
- 업무 규칙 검증은 사용자 상태, 중복 데이터, 재고와 권한처럼 저장된 정보와 함께 확인해야 합니다. 같은 요청값이라도 현재 서비스 상태에 따라 성공 여부가 달라질 수 있습니다.
- 검증 순서도 답변에 포함할 수 있습니다. 존재하지 않는 사용자의 중복 신청 여부를 먼저 조회하는 것보다 사용자 존재 여부를 확인한 뒤 신청 조건을 검사하는 편이 흐름을 이해하기 쉽습니다.
한 지원자는 회원가입 요청에서 이메일 형식만 검사하고 중복 여부는 데이터베이스 오류에 맡겼습니다. 동시 요청에서 중복 데이터 저장을 완전히 막기 위해 고유 제약 조건은 필요했지만, 사용자에게 이해할 수 있는 응답을 주기 위해 서버에서도 중복 여부를 확인했습니다. 애플리케이션 검증과 데이터베이스 제약의 역할을 나누어 설명하면서 답변의 깊이가 달라졌습니다.
- 응답은 성공 데이터와 실패 정보를 일관되게 전달해야 합니다
API 응답을 설계할 때 정상 데이터만 생각하면 오류가 발생했을 때 화면마다 처리 방식이 달라질 수 있습니다. 성공 여부, 필요한 데이터, 오류 코드와 메시지의 구조를 정하고 상황에 맞는 상태를 반환해야 합니다.
팀 프로젝트에서 회원가입은 문자열로 오류를 반환하고 로그인은 별도의 객체를 사용한 사례가 있었습니다. 프런트엔드 담당자는 기능마다 다른 조건문을 작성해야 했고 같은 오류도 서로 다른 문구로 표시했습니다. 백엔드 지원자는 팀원과 실패 응답에 필요한 항목을 논의하고 오류 코드와 메시지를 공통 구조로 정리했습니다.
- 결과 중심 설명: 예외 처리를 추가해 API의 안정성을 높였습니다.
- 협업과 설계가 담긴 설명: 회원가입과 로그인에서 실패 응답의 형태가 달라 화면 처리 코드가 반복되는 문제를 확인했습니다. 공통 오류 코드와 메시지 구조를 제안하고 중복 이메일, 잘못된 비밀번호, 만료된 인증 정보 상황에 적용한 뒤 프런트엔드와 결과를 다시 확인했습니다.
이 사례에는 서버 내부의 예외 처리뿐 아니라 API를 사용하는 클라이언트와의 계약을 조정한 경험이 들어 있습니다.
- 후속 질문은 선택 이유를 중심으로 준비해야 합니다
면접관은 왜 해당 URL과 메서드를 사용했는지, 요청 데이터를 어디까지 받았는지, 다른 응답 방식은 검토했는지 물을 수 있습니다. 정답을 외우기보다 당시 기능의 조건을 기준으로 설명해야 합니다.
게시글 수정 요청에서 사용자 ID를 요청 본문으로 받았다면 왜 인증 정보에서 확인하지 않았는지 질문받을 수 있습니다. 클라이언트가 전달한 사용자 ID만 믿으면 다른 사람의 게시글을 수정할 가능성이 있으므로 인증된 사용자와 작성자를 서버에서 비교해야 합니다.
답변을 준비할 때는 다음 내용을 확인하는 것이 좋습니다.
- 이 요청이 해결하려는 사용자 행동은 무엇인지
- 클라이언트가 전달하는 값과 서버가 확인하는 값은 무엇인지
- 정상 응답에는 어떤 데이터가 필요한지
- 잘못된 요청과 권한 부족을 어떻게 구분했는지
- 같은 기능을 다시 만든다면 무엇을 바꾸고 싶은지
이 다섯 가지를 자신의 코드와 연결하면 엔드포인트를 암기하는 답변에서 설계 의도를 설명하는 답변으로 바뀝니다.
DB구조는 테이블 개수보다 관계와 제약의 이유를 설명해야 합니다
- 데이터가 어떤 기준으로 나뉘었는지 확인해야 합니다
데이터베이스를 설계했다고 말할 때 테이블 이름만 나열하면 지원자가 어떤 판단을 했는지 알기 어렵습니다. 회원, 주문, 상품, 리뷰 테이블을 만들었다는 사실보다 데이터를 왜 분리했고 어떤 관계로 연결했는지를 설명해야 합니다.
온라인 서점 프로젝트에서 주문 정보를 하나의 테이블에 모두 저장하려던 지원자가 있었습니다. 한 주문에 여러 권의 책이 포함될 수 있다는 조건을 반영하자 주문자와 결제 상태는 주문 테이블에, 개별 책과 수량은 주문상품 테이블에 저장하는 구조가 필요했습니다. 주문 하나와 여러 주문상품의 관계를 만들고 각 항목이 어떤 데이터를 책임지는지 구분했습니다.
- 단순한 구조 설명: 회원, 도서, 주문과 주문상품 테이블을 설계했습니다.
- 관계의 이유가 보이는 설명: 한 번의 주문에 여러 도서가 포함될 수 있어 주문자와 상태는 주문 테이블에, 도서와 수량은 주문상품 테이블에 분리했습니다. 주문상품이 어느 주문에 포함되는지 외래키로 연결하고 주문 조회 시 필요한 데이터를 함께 확인했습니다.
테이블을 많이 만들었다는 설명보다 데이터의 반복을 줄이고 관계를 표현한 이유가 중요합니다.
- 현재 상품 정보와 과거 주문 기록을 구분해야 합니다
상품 가격은 시간이 지나면 변경될 수 있습니다. 주문상품이 항상 현재 상품 가격만 참조하면 과거 주문 내역의 금액도 달라져 보일 수 있습니다. 주문 당시의 상품명과 가격을 별도로 저장해야 하는 이유가 생깁니다.
쇼핑몰 프로젝트를 만든 지원자는 주문 내역을 조회할 때 상품 테이블의 현재 가격을 가져오도록 구현했습니다. 관리자 화면에서 상품 가격을 변경하자 이전 주문의 결제 금액과 조회 화면이 맞지 않는 문제가 발생했습니다. 이후 주문상품에는 주문 당시 가격을 저장하고 상품의 현재 가격과 분리했습니다.
이 경험은 데이터 중복을 무조건 없애는 것이 좋은 설계는 아니라는 점을 보여줍니다. 이력 데이터의 정확성을 유지하기 위해 필요한 값을 보존한 판단을 설명할 수 있습니다. 면접에서는 가격뿐 아니라 주문 당시 배송지와 상품명이 변경될 경우도 질문받을 수 있으므로 어떤 정보를 기록으로 남길지 정리해야 합니다.
- 제약 조건은 잘못된 데이터가 저장되는 것을 막습니다
애플리케이션 코드에서 모든 조건을 검사해도 동시에 요청이 들어오거나 다른 경로로 데이터가 저장될 수 있습니다. 데이터베이스의 고유 제약, 외래키와 null 허용 여부를 함께 고려해야 합니다.
쿠폰 발급 기능에서 서버가 발급 이력을 먼저 조회한 뒤 쿠폰을 저장하도록 구현한 사례가 있습니다. 요청이 순서대로 들어올 때는 중복이 막혔지만 같은 사용자가 짧은 시간에 여러 요청을 보내면 각각 발급 가능하다고 판단해 여러 건이 저장됐습니다.
지원자는 사용자와 쿠폰의 조합이 중복되지 않도록 고유 제약을 추가했습니다. 서버 검증은 사용자에게 이해할 수 있는 응답을 주는 역할을 하고, 데이터베이스 제약은 최종적으로 중복 저장을 막는 역할을 한다고 구분했습니다.
- 고유 제약은 이메일, 사용자별 쿠폰처럼 중복되면 안 되는 값에 적용할 수 있습니다. 단순히 설정했다는 사실보다 어떤 업무 규칙을 보호하는지 설명해야 합니다.
- 외래키는 데이터 사이의 관계를 유지하는 데 도움이 됩니다. 존재하지 않는 주문에 주문상품이 연결되지 않도록 하는 것처럼 잘못된 참조를 막는 기준이 됩니다.
- null 허용 여부는 값이 없을 수 있는 업무 상황과 연결해야 합니다. 모든 칼럼을 필수로 만들거나 편의상 비워두는 방식보다 데이터의 의미를 먼저 확인해야 합니다.
- 트랜잭션은 여러 변경의 일관성을 지키는 기준입니다
트랜잭션의 정의를 외우는 것과 프로젝트에서 필요했던 이유를 설명하는 것은 다릅니다. 하나의 사용자 행동이 여러 데이터 변경으로 이어질 때 일부만 성공하면 어떤 문제가 생기는지 찾아야 합니다.
주문 기능에서는 주문 정보 저장, 주문상품 생성, 재고 차감과 결제 상태 변경이 이어질 수 있습니다. 주문은 저장됐는데 재고 차감이 실패하면 사용자는 완료된 주문으로 볼 수 있지만 실제 상품은 확보되지 않은 상태가 됩니다.
한 지원자는 주문 저장 이후 재고가 부족하다는 예외가 발생해도 주문 데이터가 남는 문제를 발견했습니다. 관련 작업을 하나의 트랜잭션 범위로 묶고 재고 부족 상황에서 전체 변경이 이전 상태로 돌아가는지 테스트했습니다.
- 정의만 말한 답변: 트랜잭션은 여러 작업을 하나의 단위로 처리합니다.
- 프로젝트와 연결한 답변: 주문과 주문상품 저장 후 재고 차감에 실패하면 일부 데이터만 남는 문제가 있었습니다. 세 작업을 하나의 처리 범위로 묶고 재고가 부족한 요청에서 주문 데이터도 저장되지 않는지 다시 확인했습니다.
이렇게 답하면 개념과 실제 코드가 연결되고, 트랜잭션 범위를 어디까지 정했는지에 관한 질문으로도 확장할 수 있습니다.
- 조회 방식과 성능도 데이터 규모를 기준으로 설명해야 합니다
개인 프로젝트는 데이터가 적어 비효율적인 조회도 빠르게 보일 수 있습니다. 성능을 개선했다고 말하려면 어느 조건에서 문제가 나타났고 무엇으로 확인했는지 설명해야 합니다.
상품 목록과 리뷰 수를 함께 조회하는 기능에서 상품마다 별도의 쿼리가 반복된 사례가 있습니다. 데이터가 적을 때는 차이를 느끼기 어려웠지만 테스트 데이터를 늘리고 실행 로그를 확인하자 조회 횟수가 상품 수에 따라 증가했습니다. 지원자는 필요한 데이터를 한 번에 조회하는 방법을 검토하고 변경 전후의 쿼리 횟수를 비교했습니다.
데이터가 적은 프로젝트에서 큰 성능 향상을 주장하기보다 테스트 조건과 한계를 함께 밝히는 편이 신뢰를 높입니다. 실제 사용자 수가 없었다면 가상의 테스트 데이터로 확인했고 운영 환경에서는 추가 검증이 필요하다고 설명할 수 있습니다.
오류처리는 예외 문구보다 원인 추적과 복구 흐름이 중요합니다
- 실패 상황을 기능 요구사항에 포함해야 합니다
정상적인 요청만 기준으로 프로젝트를 기억하면 면접에서 예외 상황 질문이 나왔을 때 답변이 짧아집니다. 존재하지 않는 데이터, 권한 부족, 중복 요청, 외부 서비스 실패와 데이터베이스 연결 오류를 기능별로 정리해야 합니다.
파일 다운로드 기능에서는 파일 정보가 없을 수 있고, 다른 사용자의 파일에 접근할 수 있으며, 데이터베이스에는 경로가 있지만 실제 저장소에는 파일이 없을 수도 있습니다. 모든 상황을 서버 오류로 처리하면 원인을 구분하기 어렵습니다.
한 지원자는 파일 ID가 존재하는지만 확인했지만 다른 사용자의 문서도 다운로드할 수 있다는 문제를 발견했습니다. 인증된 사용자와 파일 소유자를 비교하고, 정보는 있지만 실제 파일이 없는 상황은 별도의 운영 오류로 남겼습니다. 권한 부족과 저장소 불일치를 구분하면서 보안과 운영 관점이 함께 드러났습니다.
- 로그는 추측을 확인으로 바꾸는 자료입니다
화면에서 서버 오류가 보인다고 해서 애플리케이션 코드만 문제인 것은 아닙니다. 요청 데이터, 인증 정보, 데이터베이스 연결과 외부 API 상태를 로그와 함께 확인해야 합니다.
배포한 회원가입 API에서만 오류가 발생한 지원자는 검증 로직을 여러 번 수정했습니다. 그러나 서버 로그에는 데이터베이스 인증 실패가 기록돼 있었습니다. 로컬에서 사용하던 접속 정보가 운영 환경변수에 남아 있어 배포용 데이터베이스에 연결하지 못한 것이 실제 원인이었습니다.
환경변수를 수정한 뒤 회원가입, 로그인, 중복 이메일 요청을 각각 테스트하고 애플리케이션을 재실행한 후에도 정상 연결되는지 확인했습니다. 처음의 예상이 틀렸더라도 확인 자료를 통해 원인을 바꾼 과정은 좋은 답변이 됩니다.
- 오류가 발생한 시간과 요청 조건을 먼저 고정해야 합니다. 언제든 발생한다는 표현보다 특정 사용자와 입력, 실행 환경에서 반복되는지를 확인합니다.
- 화면 메시지뿐 아니라 서버 로그와 데이터베이스 기록을 비교해야 합니다. 각 자료가 요청 흐름의 어느 구간을 보여주는지 구분하면 원인을 좁히기 쉽습니다.
- 여러 설정을 한 번에 바꾸지 않아야 합니다. 하나의 가설을 확인하고 결과를 기록해야 실제 원인이 무엇이었는지 설명할 수 있습니다.
- 클라이언트에 전달할 정보와 내부 로그를 구분해야 합니다
오류의 상세 내용을 모두 응답에 포함하면 데이터베이스 구조와 내부 경로 같은 정보가 외부에 노출될 수 있습니다. 사용자가 조치할 수 있는 메시지와 개발자가 원인을 추적할 내부 기록을 나누어야 합니다.
로그인 기능에서 비밀번호가 틀렸는지 이메일이 존재하지 않는지 모두 자세히 알려주면 특정 계정의 존재 여부를 추측하게 만들 수 있습니다. 사용자에게는 인증 정보가 일치하지 않는다는 공통 메시지를 전달하고, 내부 로그에는 추적에 필요한 범위만 남기는 방법을 고려할 수 있습니다.
반대로 회원가입의 이메일 형식 오류처럼 사용자가 직접 수정할 수 있는 문제는 구체적인 안내가 필요합니다. 모든 오류를 숨기거나 모든 상세 정보를 공개하는 것이 아니라 상황별로 필요한 정보를 결정해야 합니다.
- 외부 API 실패에도 서비스가 어떻게 동작할지 정해야 합니다
결제, 지도, 이메일과 같은 외부 서비스는 항상 성공한다고 가정할 수 없습니다. 요청 시간 초과, 잘못된 응답과 일시적인 장애가 발생했을 때 내부 데이터가 어떤 상태로 남는지 확인해야 합니다.
결제 승인 요청이 시간 초과된 주문 프로젝트에서 지원자는 곧바로 결제 실패로 처리했습니다. 그러나 실제 결제 서비스에서는 승인이 완료됐지만 응답만 늦게 도착했을 가능성이 있었습니다. 주문 상태를 바로 실패로 확정하기보다 확인이 필요한 상태로 분리하고, 결제 결과를 다시 조회하는 흐름을 검토했습니다.
모든 복잡한 결제 구조를 신입 프로젝트에서 완성할 필요는 없습니다. 다만 외부 요청 실패가 곧 업무 실패와 같은 의미인지 의심하고, 현재 구현한 범위와 추가로 필요한 처리를 구분하면 설계 관점을 보여줄 수 있습니다.
- 면접 답변은 상황과 판단, 검증의 순서로 정리해야 합니다
오류 사례를 길게 설명하더라도 순서가 없으면 핵심이 흐려집니다. 다음 구조로 정리하면 답변을 안정적으로 전달할 수 있습니다.
- 어떤 기능에서 어떤 현상이 나타났는지 설명합니다.
- 처음 예상한 원인과 실제로 확인한 자료를 말합니다.
- 확인 결과 어떤 원인으로 범위를 좁혔는지 정리합니다.
- 자신이 수정하거나 제안한 내용을 구분합니다.
- 같은 실패 조건을 다시 만들어 검증한 결과를 말합니다.
- 현재 해결하지 못한 한계가 있다면 함께 밝힙니다.
답변을 통째로 암기하기보다 이 순서를 기억하면 다른 프로젝트 질문에도 적용할 수 있습니다. 면접관이 대안을 묻더라도 당시 조건과 선택 기준을 바탕으로 설명하기 쉬워집니다.
- conclusion
백엔드 프로젝트를 면접 답변으로 바꾸려면 구현한 기능을 다시 나열하는 데서 멈추지 않아야 합니다. API에서는 사용자의 요청 목적과 입력 검증, 성공과 실패 응답을 확인해야 합니다. 데이터베이스에서는 테이블을 나눈 이유와 관계, 제약 조건과 트랜잭션 범위를 설명할 수 있어야 합니다. 오류 상황에서는 처음의 추측보다 로그와 데이터로 원인을 좁히고 수정 후 재검증한 과정이 중요합니다.
- API 하나를 선택해 사용자 행동, 요청값, 서버 검증, 정상 응답과 실패 응답을 순서대로 말해 보세요. 각 단계의 이유를 설명하지 못한다면 코드를 다시 확인할 필요가 있습니다.
- 핵심 테이블의 관계를 직접 그려보고 데이터를 분리한 이유를 적어 보세요. 중복을 막는 제약과 여러 저장 작업의 일관성을 어떻게 지켰는지도 함께 점검해야 합니다.
- 대표적인 오류 하나를 발생 조건, 확인한 로그, 실제 원인, 수정 내용과 재검증 결과로 정리해 보세요. 해결하지 못한 한계도 구분하면 경험의 범위가 더 신뢰 있게 전달됩니다.
실제 프로젝트를 검토해 보면 기능이 부족해서 답변이 짧은 경우보다 구현 당시의 판단을 기록하지 않아 설명하지 못하는 경우가 많았습니다. 게시판처럼 익숙한 결과물이라도 권한 검증과 데이터 관계, 존재하지 않는 ID의 응답을 구체적으로 다뤘다면 충분한 면접 사례가 됩니다. 반대로 기능이 많아도 정상 실행만 확인했다면 후속 질문에서 깊이를 보여주기 어렵습니다. 코드를 다시 읽고 요청, 데이터, 실패 흐름을 복기한 기록은 README의 개선 사례가 되고 기술면접에서 실무 감각과 문제해결 능력을 보여주는 근거가 됩니다.