백엔드 면접에서 자주 나오는 질문 38가지 정리

 ・ 18 min

photo by Matthew Smith(https://unsplash.com/@whale?utm_source=templater_proxy&utm_medium=referral) on Unsplash

면접을 보다 보면 같은 주제가 계속 반복되죠. Java/Spring 기반 백엔드 면접에서 자주 나오는 질문들을 주제별로 묶어서 정리해봤어요. 실제로 받았던 질문 38개를 토대로, 어떻게 답해야 할지 함께 짚어볼게요.

Java 기초#

Java의 특징#

객체지향의 핵심 3가지로 답하면 안전해요.

  1. 캡슐화(Encapsulation) — 데이터와 동작을 하나로 묶고, 외부에는 필요한 것만 공개해요.
  2. 상속(Inheritance) — 부모 클래스의 속성과 동작을 자식 클래스가 물려받아요.
  3. 다형성(Polymorphism) — 같은 인터페이스로 다양한 구현을 호출할 수 있어요.

여기에 추상화(Abstraction) 까지 묶어 4가지로 말하기도 해요. 그리고 JVM 위에서 동작해서 플랫폼 독립적(Write Once, Run Anywhere) 이라는 점, 가비지 컬렉션으로 메모리를 자동 관리한다는 점도 같이 언급하면 좋아요.

요즘은 면접에서 최신 Java 기능도 자주 물어요. Java 21(LTS) 이후의 핵심 기능 정도는 알아두면 좋아요.

  • Record (Java 16+) — 불변 데이터 클래스를 한 줄로 선언
  • Sealed Class (Java 17+) — 상속 가능한 클래스를 명시적으로 제한
  • Pattern Matching (Java 21+)switchinstanceof에 패턴 매칭 적용
  • Virtual Threads (Java 21+) — Project Loom의 결과물, 경량 스레드로 동시성 처리

클래스와 객체#

  • 클래스(Class) — 객체를 만들기 위한 설계도. 속성(필드)과 동작(메서드)을 정의해요.
  • 객체(Object) — 클래스로부터 만들어진 실제 메모리에 올라간 실체.

붕어빵 틀이 클래스라면, 그 틀로 찍어낸 붕어빵 하나하나가 객체예요.

데이터 타입 (원시형 vs 참조형)#

원시형(Primitive) — 값 자체를 스택에 저장해요. byte, short, int, long, float, double, char, boolean 8가지.

참조형(Reference) — 힙에 저장된 객체의 주소를 가져요. 클래스, 배열, 인터페이스 등이 모두 참조형이에요.

int a = 10;              // 원시형: 값 10이 직접 저장
String s = "hello";      // 참조형: 힙의 객체 주소를 저장

인스턴스화#

클래스라는 설계도로 객체를 실제로 메모리에 만드는 행위예요. new 키워드를 사용해요.

User user = new User();  // User 클래스를 인스턴스화

이때 만들어진 user를 클래스의 인스턴스라고 불러요. 객체와 거의 같은 의미지만, "어떤 클래스의 인스턴스인지"를 강조할 때 인스턴스라는 표현을 써요.

인터페이스 vs 추상클래스#

구분 인터페이스 추상클래스
목적 구현 강제 (계약) 공통 기능 + 일부 강제
다중 상속 가능 불가능 (단일 상속)
필드 public static final 모든 종류 가능
메서드 추상 + default + static 추상 + 일반
사용 시점 "무엇을 할 수 있는가" "무엇이다 (is-a)"

인터페이스는 "이 클래스는 이런 동작을 할 수 있다"는 계약이에요. 추상클래스는 공통 구현을 자식에게 물려주면서, 일부 메서드는 자식이 반드시 구현하도록 강제할 때 써요.

제네릭(Generic)을 왜 쓰나요#

한 줄로 답하면 타입 안전성 + 코드 재사용이에요.

// 제네릭 없이
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0);  // 캐스팅 필요, 런타임 에러 위험
 
// 제네릭 사용
List<String> list = new ArrayList<>();
list.add("hello");
String s = list.get(0);  // 캐스팅 불필요, 컴파일 시점에 타입 체크

런타임에 터질 수 있는 ClassCastException컴파일 시점에 잡아주는 것이 가장 큰 이득이에요.

컬렉션 프레임워크#

큰 그림#

크게 3가지 인터페이스로 나뉘어요.

  • List — 순서 있음, 중복 허용 (ArrayList, LinkedList)
  • Set — 순서 없음, 중복 제거 (HashSet, TreeSet, LinkedHashSet)
  • Map키-값 형태 (HashMap, TreeMap, LinkedHashMap)

MapCollection 인터페이스를 상속하지 않지만, 컬렉션 프레임워크에 포함돼요.

Java 21에서 Sequenced Collections가 추가됐어요. SequencedCollection, SequencedSet, SequencedMap 인터페이스로 첫/마지막 원소에 일관된 API(getFirst(), getLast(), addFirst(), addLast(), reversed())를 제공해요. 기존에는 LinkedList, LinkedHashSet, LinkedHashMap마다 첫/마지막 접근 방식이 제각각이었거든요.

Array vs ArrayList#

항목 Array ArrayList
크기 고정 가변
타입 원시형/참조형 모두 참조형만 (제네릭)
성능 빠름 (가벼움) 약간 오버헤드
기능 최소한 add, remove, contains 등 풍부

내부적으로 ArrayList도 배열로 구현되어 있어요. 다만 용량이 부족하면 더 큰 배열을 새로 만들어 복사하는 식으로 가변 크기를 흉내내요.

키-값 / 중복 제거#

  • 키-값: Map 계열 → HashMap, TreeMap, LinkedHashMap
  • 중복 제거: Set 계열 → HashSet, TreeSet, LinkedHashSet

for-each에 쓸 수 있는 타입#

Iterable 인터페이스를 구현한 모든 타입이에요. 배열도 가능하고요.

public interface Iterable<T> {
    Iterator<T> iterator();
}

CollectionIterable을 상속하므로 List, Set 모두 가능해요. Map은 직접 안 되고, entrySet(), keySet(), values()로 변환해서 써야 해요.

Spring & DI/IoC#

DI와 IoC#

  • IoC (Inversion of Control) — 제어의 역전. 객체의 생성과 생명주기 관리를 개발자가 아니라 프레임워크(컨테이너)가 담당해요.
  • DI (Dependency Injection) — 의존성 주입. 객체가 필요한 다른 객체를 직접 만들지 않고 외부에서 주입받아요. IoC를 구현하는 방법 중 하나예요.
// DI 없이
public class OrderService {
    private UserRepository repo = new UserRepository();  // 강결합
}
 
// DI 적용
public class OrderService {
    private final UserRepository repo;
 
    public OrderService(UserRepository repo) {  // 외부에서 주입
        this.repo = repo;
    }
}

장점은 결합도 감소, 테스트 용이성, 코드 재사용성 향상이에요.

Spring Bean Scope#

  • singleton (기본값) — 컨테이너당 하나의 인스턴스
  • prototype — 요청할 때마다 새 인스턴스
  • request — HTTP 요청당 하나 (웹 전용)
  • session — HTTP 세션당 하나 (웹 전용)
  • application — ServletContext당 하나 (웹 전용)

대부분의 서비스 빈은 stateless하게 설계해서 singleton으로 충분해요.

Spring 버전#

2026년 시점에서 실무 표준은 Spring Boot 3.x / Spring Framework 6.x 예요. 핵심 변경점만 짚으면:

  • Java 17 이상 필수 (Spring Boot 3.2부터는 Java 21 정식 지원, Virtual Threads 활용 가능)
  • javax.*jakarta.* 패키지 변경 (Jakarta EE 9 전환). 가장 큰 마이그레이션 이슈예요.
  • GraalVM Native Image 정식 지원
  • Observability 표준 (Micrometer + OpenTelemetry)

Spring Boot 2.x를 쓰던 프로젝트는 이 마이그레이션 이슈로 면접에서 자주 나와요.

REST 표준 어노테이션 (HATEOAS / OpenAPI)#

면접에서 "스프링 API 어노테이션이 표준에 맞지 않는데, 표준에 맞추려면?"이라는 질문은 보통 두 가지 중 하나를 묻는 거예요.

  1. HATEOAS (Hypermedia As The Engine Of Application State) — Roy Fielding이 정의한 진정한 REST의 마지막 단계. Spring에서는 Spring HATEOAS 라이브러리를 써요.
  2. OpenAPI 3 / Swagger — API 명세 표준. Spring에서는 springdoc-openapi가 사실상 표준 (예전에 쓰던 SpringFox는 deprecated).

HATEOAS는 응답에 다음 액션으로 갈 수 있는 링크(href) 를 포함해서 진정한 REST에 가까워지게 만들어요.

{
  "id": 1,
  "name": "User",
  "_links": {
    "self": { "href": "/users/1" },
    "orders": { "href": "/users/1/orders" }
  }
}

실무에서 HATEOAS 풀 적용은 드물고, OpenAPI로 API 명세를 표준화하는 쪽이 훨씬 보편적이에요.

HTTP & REST#

HTTP#

  • HyperText Transfer Protocol — 웹에서 클라이언트-서버가 통신하는 프로토콜
  • 무상태(Stateless) — 각 요청은 독립적이고, 서버는 이전 요청을 기억하지 않아요
  • 요청-응답 구조: 메서드, URL, 헤더, 바디로 구성
  • 기본 포트 80, HTTPS는 443

버전별 핵심 차이도 알아두면 좋아요.

버전 특징 전송
HTTP/1.1 텍스트 기반, Keep-Alive TCP
HTTP/2 바이너리, 멀티플렉싱, 헤더 압축(HPACK), 서버 푸시 TCP + TLS
HTTP/3 QUIC 위에서 동작, 0-RTT 연결, TCP 헤드 오브 라인 블로킹 해결 UDP + TLS 1.3

2026년 시점에는 주요 CDN과 대형 서비스 대부분이 HTTP/3을 활성화한 상태예요.

HTTP 메서드와 안전성/멱등성#

메서드 Safe (안전) Idempotent (멱등) 용도
GET 조회
HEAD 헤더만 조회
OPTIONS 지원 메서드 확인
PUT 전체 수정/생성
DELETE 삭제
POST 생성
PATCH 부분 수정
  • Safe — 서버 상태를 변경하지 않아요 (조회만)
  • Idempotent — 여러 번 요청해도 결과가 같아요

HTTPS#

HTTP에 TLS/SSL 암호화를 추가한 프로토콜이에요. 핵심 보장 3가지를 기억해두세요.

  1. 기밀성(Confidentiality) — 전송 데이터 암호화
  2. 무결성(Integrity) — 중간에 데이터가 변조되지 않았음을 보장
  3. 인증(Authentication) — 서버가 진짜 그 서버인지 인증서로 확인

핸드셰이크 과정은 비대칭키로 세션키를 안전하게 교환 → 이후 통신은 빠른 대칭키로 진행돼요.

지금은 TLS 1.3가 표준이에요. 1-RTT 핸드셰이크로 속도가 빨라졌고, 취약하다고 평가된 RSA 키 교환·CBC·SHA-1 같은 알고리즘이 제거됐어요. TLS 1.0/1.1은 deprecate된 지 오래고, TLS 1.2도 점진적으로 정리되는 추세예요.

RESTful API#

REST는 REpresentational State Transfer 의 약자예요. 핵심 원칙은 다음과 같아요.

  • 자원(Resource)을 URL로 식별/users/1
  • 행위는 HTTP 메서드로 표현 — GET, POST, PUT, DELETE
  • 무상태성(Stateless) — 서버가 클라이언트 상태를 저장하지 않음
  • 표현(Representation) — JSON, XML 등으로 자원 상태 전달

URL에는 명사만, 행동은 HTTP 메서드로.

나쁜 예: GET /getUser/1
좋은 예: GET /users/1
 
나쁜 예: POST /deleteUser/1
좋은 예: DELETE /users/1

OSI 7계층과 HTTP#

계층 이름 예시
7 애플리케이션 HTTP, FTP, SMTP, DNS
6 표현 SSL/TLS, JPEG, ASCII
5 세션 NetBIOS, RPC
4 전송 TCP, UDP
3 네트워크 IP, ICMP
2 데이터 링크 Ethernet, MAC
1 물리 케이블, 신호

HTTP는 7계층(애플리케이션), TCP는 4계층, IP는 3계층에서 동작해요. HTTPS의 TLS는 보통 6계층(표현)으로 분류해요.

웹 서버 & 서블릿#

웹 서버 vs WAS#

  • 웹 서버 (Web Server) — 정적 콘텐츠(HTML, CSS, 이미지) 처리. Nginx, Apache HTTPD.
  • WAS (Web Application Server) — 동적 콘텐츠 처리, 비즈니스 로직 실행. Tomcat, Jetty.

실무에서는 웹 서버 앞단(Nginx) + WAS 뒷단(Tomcat) 구조를 많이 써요. 정적 자원은 웹 서버가, 동적 처리는 WAS가 담당해서 부하를 분산해요.

서블릿(Servlet) 동작 방식#

  1. 클라이언트 요청이 들어와요
  2. 서블릿 컨테이너(Tomcat 등)가 요청을 받아요
  3. URL 매핑으로 해당 서블릿을 찾아요 (없으면 인스턴스화)
  4. 스레드를 하나 할당해서 service()doGet() / doPost() 호출
  5. 응답 생성 후 클라이언트로 전달

서블릿은 싱글톤으로 동작해서, 여러 요청이 같은 인스턴스를 공유해요. 그래서 인스턴스 변수에 상태를 저장하면 위험해요.

Jakarta EE 9 이후 패키지가 javax.servletjakarta.servlet으로 바뀌었고, Spring Boot 3.x도 이 변화를 따라가요. 또한 Spring Boot 3.2부터 Virtual Threads를 활성화하면 요청당 스레드 모델의 비용이 크게 낮아져요. 비동기/논블로킹이 필요하면 Spring WebFlux(리액티브) 선택지도 있어요.

서블릿 필터(Filter)#

서블릿 앞단에서 요청과 응답을 가로채는 컴포넌트예요. 인증, 로깅, 인코딩 변환, CORS 처리 등에 써요.

클라이언트 → 필터 1 → 필터 2 → 서블릿 → 필터 2 → 필터 1 → 클라이언트

Spring의 Interceptor와 비슷하지만 적용 시점이 달라요. Filter는 DispatcherServlet 이전, Interceptor는 DispatcherServlet 이후 컨트롤러 이전에서 동작해요.

쿠키 vs 세션#

항목 쿠키 (Cookie) 세션 (Session)
저장 위치 클라이언트 (브라우저) 서버
보안 상대적으로 취약 안전
용량 4KB 제한 서버 메모리 한도
만료 설정한 시간 브라우저 종료 시 (또는 timeout)

민감한 정보는 세션, 단순한 식별자나 자동 로그인 토큰은 쿠키에 저장하는 게 일반적이에요. 자동 로그인을 쿠키에 두는 이유는 브라우저를 닫아도 유지되어야 하기 때문이에요. 세션은 브라우저 종료/timeout으로 사라지니까요.

쿠키 보안 속성은 다음 정도는 알아두는 게 좋아요.

  • HttpOnly — JavaScript에서 접근 불가 (XSS 방어)
  • Secure — HTTPS에서만 전송
  • SameSiteStrict / Lax / None. CSRF 방어용. Chrome은 기본 Lax 가 표준이 됐어요.
  • Partitioned (CHIPS) — 서드파티 쿠키 격리, 추적 방지

요즘은 세션 대신 JWT(JSON Web Token) 같은 stateless 토큰 인증도 많이 써요. 서버 메모리를 점유하지 않아 수평 확장에 유리하지만, 토큰 폐기(revoke)가 어렵다는 단점이 있어요. 그래서 짧은 access token + 긴 refresh token 조합으로 운영해요.

보안#

SQL Injection 방어#

가장 확실한 방법은 PreparedStatement (파라미터 바인딩) 예요.

// 위험: 문자열 결합
String sql = "SELECT * FROM users WHERE id = '" + userId + "'";
 
// 안전: 파라미터 바인딩
String sql = "SELECT * FROM users WHERE id = ?";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1, userId);

MyBatis나 JPA를 쓰면 자동으로 막아주지 않나요? — 부분적으로 맞지만 완전히 안전하지는 않아요.

  • MyBatis의 #{}는 PreparedStatement로 변환되어 안전, ${}는 문자열 치환이라 위험
  • JPA의 JPQL은 안전하지만, Native Query에서 문자열을 결합하면 똑같이 위험

결론: ORM을 써도 동적으로 SQL 일부를 문자열로 만들 때는 항상 조심해야 해요.

프록시 vs 게이트웨이#

  • 프록시(Proxy) — 클라이언트와 서버 사이의 중개자. 같은 프로토콜에서 단순히 요청을 전달.
    • Forward Proxy — 클라이언트 측 (사내망 → 외부)
    • Reverse Proxy — 서버 측 (외부 → 내부 서버 분산, Nginx)
  • 게이트웨이(Gateway)프로토콜 변환, 라우팅, 인증, 로깅 등 부가 기능까지 수행. API Gateway가 대표적.

요약하면 프록시는 단순 중개, 게이트웨이는 트래픽의 진입점에서 다양한 정책을 적용해요.

MSA 시대에 들어서면서 Service Mesh(Istio, Linkerd)도 자주 거론돼요. 사이드카 프록시(Envoy 등)를 통해 서비스 간 통신을 인프라 레벨에서 처리해요. mTLS, 트래픽 분산, 관측성 같은 횡단 관심사를 애플리케이션 코드 밖으로 빼는 게 핵심이에요.

메시지 큐 & 캐시#

RabbitMQ 특징#

  • AMQP(Advanced Message Queuing Protocol) 기반
  • Producer → Exchange → Queue → Consumer 구조
  • 메시지 영속성 지원 (디스크 저장)
  • 유연한 라우팅 (Exchange 타입에 따라)
  • 다양한 언어 클라이언트 지원

RabbitMQ Exchange 타입 (큐 외 다른 방식)#

큐에 직접 쌓는 게 아니라, Exchange가 라우팅 규칙에 따라 큐로 분배해요.

Exchange 타입 동작
Direct 라우팅 키와 정확히 일치하는 큐로
Topic 라우팅 키 패턴 매칭 (*, #)
Fanout 모든 바인딩된 큐로 브로드캐스트
Headers 메시지 헤더 기준 라우팅

Topic Exchange가 흔히 말하는 "토픽에 쏘는" 방식이에요. order.created, order.cancelled 같은 패턴 라우팅에 유용해요.

참고로 RabbitMQ 4.x부터는 Streams라는 새로운 영속 로그 자료구조도 지원해서 Kafka처럼 재처리·롱리텐션이 필요한 시나리오에도 쓸 수 있어요. 다만 대용량 이벤트 스트리밍은 여전히 Kafka가 강세고, RabbitMQ는 신뢰성 있는 작업 큐로 더 많이 채택돼요.

Redis 외 캐시 (인메모리 캐시)#

질문이 "램캐시"였다면 로컬 인메모리 캐시를 묻는 거예요.

  • Caffeine — Java에서 가장 많이 쓰는 고성능 인메모리 캐시
  • Ehcache — Java 진영 전통의 강자, 분산 캐시도 지원
  • Guava Cache — Caffeine의 전신, 지금은 Caffeine 권장
  • Memcached — 분산 인메모리 캐시 (Redis보다 단순)

Redis는 별도 서버 + 네트워크, Caffeine은 JVM 내부에서 동작해요. 같은 머신 내 빠른 캐시가 필요하면 Caffeine, 여러 서버가 공유해야 하면 Redis가 좋아요.

JavaScript#

Minify를 왜 하나요#

  • 파일 크기 감소 → 다운로드 속도 향상
  • 네트워크 비용 절약
  • 파싱 시간 단축

공백, 줄바꿈, 주석을 제거하고 변수명을 짧게 바꿔요. 보안 목적은 아니에요 (난독화는 다른 개념).

요즘 빌드는 거의 esbuild, swc, Rolldown 같은 Go/Rust 기반 도구가 처리해요. 기존 Webpack/Babel에 비해 수십 배 빠른 번들·트랜스파일이 가능해서 Vite, Turbopack 같은 차세대 번들러의 표준이 됐어요.

인라인 vs 외부 파일#

항목 인라인 (<script>...</script>) 외부 파일 (<script src="...">)
HTTP 요청 추가 요청 없음 추가 요청 발생
캐싱 캐시 불가 브라우저 캐싱 가능
재사용 페이지마다 중복 여러 페이지에서 공유
유지보수 어려움 쉬움
초기 렌더링 빠를 수 있음 첫 요청은 느림

HTTP/1.1 시절에는 외부 파일 요청 비용이 부담스러웠지만, HTTP/2 이후 멀티플렉싱으로 외부 파일을 여러 개 가져와도 큰 손해가 없어졌어요. 그래서 세분화된 모듈을 외부 파일로 분리하고, 첫 화면에 꼭 필요한 critical CSS/JS만 인라인하는 방식이 권장돼요.

인라인 스크립트의 위치#

  • <head> — 페이지 렌더링 전에 실행, DOM 접근 불가능
  • <body> — DOM 로드 후 실행, 일반적으로 권장
  • <head> + defer/async — 다운로드는 병렬, 실행 시점 제어

DOM을 다루는 코드라면 </body> 직전이 안전해요. 또는 DOMContentLoaded 이벤트를 사용해요.

요즘은 <head>에 두고 defer 또는 type="module" 을 붙이는 게 모던 베스트 프랙티스예요. 둘 다 HTML 파싱을 막지 않으면서 DOM 준비 후 순서대로 실행되거든요.

<!-- 모던 권장 패턴 -->
<script type="module" src="/app.js"></script>
<script defer src="/legacy.js"></script>

협업과 커뮤니케이션#

의견이 다를 때 어떻게 하나요#

이런 질문은 태도와 사고 과정을 보는 거예요. 모범 답안은 다음 흐름이에요.

  1. 상대방 의견을 끝까지 듣기 — 의도와 근거를 정확히 이해
  2. 공통의 목표를 확인 — "우리 둘 다 X를 원하는 거 맞죠?"
  3. 데이터/근거 기반으로 토론 — 감정이 아닌 객관적 근거
  4. 정해진 결정에는 따르기 (Disagree and Commit) — 결정 후에는 100% 협조

핵심은 "상대를 이기는 게 아니라 더 나은 결과를 내는 것" 이라는 자세예요.

마무리#

면접 질문 하나하나가 결국 "기본기를 얼마나 정확하게 알고 있는가" 를 보는 도구예요. 키워드만 나열하는 답변보다는, 왜 그런지 + 실무에서 어떻게 쓰는지를 함께 말할 수 있도록 준비하는 게 좋아요.

모르는 질문이 나오면 솔직하게 "정확히는 모르지만, 이런 맥락에서 들어본 적 있습니다"라고 인정하는 것도 큰 점수가 돼요. 모르는 걸 아는 척하는 게 가장 위험하거든요.


People take different roads seeking fulfilment and happiness. Just because theyre not on your road doesn't mean they've gotten lost.

— Dalai Lama


다른 글
백엔드 면접 질문 25개로 돌아보는 자바 서버 기초 커버 이미지
 ・ 13 min

백엔드 면접 질문 25개로 돌아보는 자바 서버 기초

정부지원사업과 투자, 창업자가 꼭 알아야 할 것들 커버 이미지
 ・ 3 min

정부지원사업과 투자, 창업자가 꼭 알아야 할 것들

백엔드 면접 준비를 위한 핵심 개념 학습 정리 커버 이미지
 ・ 9 min

백엔드 면접 준비를 위한 핵심 개념 학습 정리