
Java / OOP, HTTP / Web, Spring, JPA, 인증 영역에서 자주 나올 수 있는 면접 질문과 핵심 답변을 정리했습니다.
A.
OOP의 4가지 특징은 캡슐화, 상속, 다형성, 추상화입니다.
완전히 같은 개념은 아닙니다.
은닉화는 필드를 private으로 막는 것처럼 내부 데이터를 숨기는 개념이고,
캡슐화는 데이터와 메서드를 객체 안에 묶고 필요한 기능만 외부에 제공하는 더 넓은 개념입니다.
예를 들어 PaymentService가 KakaoPay, TossPay 같은 구체 클래스가 아니라 PaymentClient 인터페이스에 의존하게 만들 수 있습니다.
그러면 결제 방식이 바뀌어도 서비스 코드는 크게 변경하지 않고 구현체만 교체할 수 있습니다.
A.
오버로딩은 같은 이름의 메서드를 매개변수 개수나 타입을 다르게 해서 여러 개 정의하는 것입니다.
오버라이딩은 부모 클래스나 인터페이스의 메서드를 자식 클래스에서 재정의하는 것입니다.
| 구분 | 오버로딩 | 오버라이딩 |
|---|---|---|
| 기준 | 메서드 이름 같음 | 부모 메서드 재정의 |
| 매개변수 | 달라야 함 | 같아야 함 |
| 반환 타입 | 자유로움 | 같거나 하위 타입 |
| 시점 | 컴파일 시점 | 런타임 시점 |
@Override를 붙이지 않아도 동작하는데 왜 붙이나요?@Override를 붙이면 컴파일러가 실제로 오버라이딩이 맞는지 검사해줍니다.
메서드 이름이나 파라미터를 실수로 잘못 작성했을 때 컴파일 단계에서 오류를 잡을 수 있어서 안정성이 높아집니다.
super.메서드명()을 사용합니다.
예를 들어 super.print()처럼 호출하면 부모 클래스의 원래 메서드를 실행할 수 있습니다.
A.
인터페이스는 객체가 반드시 구현해야 하는 기능의 규칙을 정의할 때 사용합니다.
추상 클래스는 공통 필드나 공통 로직을 자식 클래스에 물려주면서 일부 메서드는 자식이 직접 구현하게 할 때 사용합니다.
| 구분 | 인터페이스 | 추상 클래스 |
|---|---|---|
| 목적 | 기능 명세, 규칙 정의 | 공통 로직 재사용 |
| 다중 구현 | 가능 | 불가능 |
| 필드 | 상수 중심 | 인스턴스 필드 가능 |
| 생성자 | 없음 | 있음 |
아닙니다.
인터페이스에 default 메서드가 추가되어 기본 구현은 가능해졌지만, 인터페이스는 여전히 상태를 가지는 인스턴스 필드나 생성자를 가질 수 없습니다.
추상 클래스는 공통 상태와 공통 로직을 함께 관리할 수 있다는 차이가 있습니다.
Java는 클래스 다중 상속을 지원하지 않지만, 인터페이스는 여러 개 구현할 수 있습니다.
그래서 여러 역할이나 기능을 동시에 부여해야 할 때 인터페이스를 사용합니다.
A.
| 구분 | 특징 |
|---|---|
| List | 순서 있음, 중복 허용 |
| Set | 중복 불가 |
| Map | Key-Value 저장, Key 중복 불가 |
ArrayList는 배열 기반이라 인덱스 조회가 빠릅니다.
O(1)O(n)LinkedList는 노드 연결 구조라 중간 삽입/삭제에 유리할 수 있지만, 특정 위치까지 이동해야 하므로 조회가 느립니다.
O(n)O(1)일반적인 조회가 많으면 ArrayList를 주로 사용합니다.
HashMap은 먼저 hashCode()로 저장 위치를 찾고, 같은 해시 위치에 값이 있으면 equals()로 실제 같은 Key인지 비교합니다.
즉, Key 중복 판단에는 hashCode()와 equals()가 함께 사용됩니다.
A.
GET은 주로 데이터를 조회할 때 사용하고, 요청 데이터는 보통 URL의 Query Parameter로 전달합니다.
POST는 데이터를 생성하거나 처리할 때 사용하고, 요청 데이터는 주로 Body에 담아 전달합니다.
| 구분 | GET | POST |
|---|---|---|
| 목적 | 조회 | 생성, 처리 |
| 데이터 전달 | Query Parameter | Body |
| 멱등성 | 일반적으로 있음 | 일반적으로 없음 |
| 캐싱 | 가능 | 보통 캐싱하지 않음 |
기술적으로 Body를 담는 것이 완전히 불가능한 것은 아닙니다.
하지만 HTTP 표준 관점에서 일반적인 사용 방식이 아니고, 서버나 프록시, 라이브러리에서 Body를 무시할 수 있기 때문에 권장하지 않습니다.
복잡한 검색 조건이라면 POST /search처럼 Body에 검색 조건을 담는 방식으로 설계할 수 있습니다.
단, 이 경우에도 실제 의미는 조회이므로 API 이름과 문서에서 검색용 요청이라는 것을 명확히 하는 것이 좋습니다.
A.
| 상태코드 | 의미 |
|---|---|
| 200 OK | 요청 성공 |
| 201 Created | 리소스 생성 성공 |
| 400 Bad Request | 잘못된 요청 |
| 404 Not Found | 리소스를 찾을 수 없음 |
| 500 Internal Server Error | 서버 내부 오류 |
201 Created는 새로운 리소스가 생성되었다는 의미를 더 명확하게 전달합니다.
따라서 생성 API에서는 200 OK보다 201 Created가 RESTful한 의미에 더 잘 맞습니다.
단건 조회에서 특정 ID의 리소스가 없다면 404 Not Found가 더 적절합니다.
반면 목록 조회 결과가 비어 있는 경우에는 요청 자체는 성공한 것이므로 200 OK와 빈 배열을 반환하는 것이 자연스럽습니다.
A.
Cookie는 클라이언트 브라우저에 저장되는 데이터입니다.
Session은 서버에 사용자 상태를 저장하고, 클라이언트는 Session ID만 Cookie로 들고 있습니다.
| 구분 | Cookie | Session |
|---|---|---|
| 저장 위치 | 클라이언트 | 서버 |
| 보안 | 상대적으로 취약 | 상대적으로 안전 |
| 서버 부담 | 적음 | 있음 |
| 사용 예시 | 자동 로그인, 사용자 설정 | 로그인 상태 관리 |
서버마다 세션 저장소가 다르면 사용자가 처음 로그인한 서버가 아닌 다른 서버로 요청했을 때 세션을 찾지 못할 수 있습니다.
이를 해결하기 위해 Redis 같은 중앙 세션 저장소를 사용하거나 Sticky Session을 사용할 수 있습니다.
HttpOnly는 JavaScript에서 쿠키에 접근하지 못하게 해서 XSS 공격으로 쿠키가 탈취되는 위험을 줄입니다.
Secure는 HTTPS 통신에서만 쿠키가 전송되도록 해서 네트워크 구간에서 쿠키가 노출되는 위험을 줄입니다.
A.
3 Layer Architecture는 역할을 분리해서 코드의 유지보수성과 테스트 편의성을 높이기 위해 사용합니다.
각 계층의 책임이 분리되면 변경이 필요한 부분을 찾기 쉽고, 테스트하기도 쉬워집니다.
Service끼리 무분별하게 호출하면 의존 관계가 복잡해지고 순환 참조가 발생할 수 있습니다.
또한 하나의 Service가 너무 많은 책임을 가지게 되어 유지보수가 어려워질 수 있습니다.
여러 도메인이 엮이는 로직은 Facade 계층을 두어 흐름을 조율하는 방식도 사용할 수 있습니다.
Controller가 요청 처리뿐만 아니라 비즈니스 로직까지 담당하게 되어 책임이 커집니다.
그 결과 코드 중복이 생기고, 테스트가 어려워지며, API 변경 시 비즈니스 로직까지 영향을 받을 수 있습니다.
A.
IoC는 객체 생성과 의존성 관리의 제어권을 개발자가 아니라 Spring 컨테이너가 가지는 것입니다.
DI는 필요한 의존 객체를 직접 생성하지 않고 외부에서 주입받는 방식입니다.
생성자 주입이 권장되는 이유는 다음과 같습니다.
final로 선언할 수 있음필드 주입은 외부에서 의존성을 주입하기 어렵기 때문에 테스트가 불편합니다.
또한 의존성이 숨겨져 있어 객체가 어떤 의존성을 필요로 하는지 명확히 드러나지 않습니다.
순환 의존성은 A가 B를 필요로 하고, B도 다시 A를 필요로 하는 구조입니다.
생성자 주입은 객체 생성 시점에 모든 의존성이 필요하기 때문에 순환 구조가 있으면 애플리케이션 시작 단계에서 바로 오류가 발생합니다.
A.
Spring Bean은 Spring 컨테이너가 생성하고 관리하는 객체입니다.
Spring은 기본적으로 Bean을 싱글톤으로 관리합니다.
그 이유는 매 요청마다 객체를 새로 만들지 않고 하나의 객체를 재사용해서 메모리 사용을 줄이고 성능을 높이기 위해서입니다.
싱글톤 Bean은 여러 요청이 동시에 공유합니다.
따라서 인스턴스 필드에 사용자별 상태를 저장하면 다른 요청과 데이터가 섞이는 동시성 문제가 발생할 수 있습니다.
그래서 Service나 Controller Bean은 보통 상태를 가지지 않도록 설계합니다.
@Component와 @Bean의 차이는 무엇인가요?@Component는 클래스에 붙여서 Spring이 자동으로 스캔해 Bean으로 등록하게 합니다.
@Bean은 설정 클래스에서 메서드에 붙여 직접 객체를 생성하고 Bean으로 등록할 때 사용합니다.
| 구분 | 사용 상황 |
|---|---|
@Component | 내가 만든 클래스를 자동 등록할 때 |
@Bean | 외부 라이브러리 객체를 Bean으로 등록할 때 |
A.
DTO는 계층 간 데이터를 전달하기 위한 객체입니다.
Entity를 직접 반환하면 다음과 같은 문제가 생길 수 있습니다.
따라서 API 요청과 응답에는 Entity 대신 DTO를 사용하는 것이 좋습니다.
요청 데이터와 응답 데이터의 목적이 다르기 때문입니다.
RequestDTO는 클라이언트가 보내는 값을 검증하는 데 집중하고,
ResponseDTO는 클라이언트에게 보여줄 데이터만 담는 데 집중합니다.
@JsonIgnore를 붙여 민감한 필드를 숨기면 DTO 없이도 되지 않나요?간단한 경우에는 가능할 수 있지만 권장되지는 않습니다.
@JsonIgnore는 직렬화 문제만 일부 해결할 뿐, API 응답 구조와 Entity 구조가 강하게 묶이는 문제는 해결하지 못합니다.
DTO를 사용하면 API 스펙과 DB 모델을 분리할 수 있습니다.
A.
영속성 컨텍스트는 JPA가 Entity를 관리하는 1차 캐시 공간입니다.
EntityManager가 Entity를 저장하고 관리하는 영역이라고 볼 수 있습니다.
주요 이점은 다음과 같습니다.
처음 조회할 때만 SQL이 실행되고, 두 번째 조회는 영속성 컨텍스트의 1차 캐시에서 가져옵니다.
따라서 같은 트랜잭션 안에서는 같은 ID의 Entity에 대해 동일성이 보장됩니다.
준영속 상태는 한 번 영속성 컨텍스트에서 관리되었지만 현재는 분리되어 관리되지 않는 상태입니다.
예를 들어 트랜잭션이 끝나거나, detach(), clear(), close()가 호출되면 Entity가 준영속 상태가 될 수 있습니다.
A.
더티 체킹은 영속성 컨텍스트가 관리 중인 Entity의 변경 사항을 감지해서 트랜잭션 커밋 시점에 자동으로 UPDATE 쿼리를 실행하는 기능입니다.
JPA는 Entity를 처음 조회했을 때의 스냅샷을 가지고 있다가, 커밋 시점에 현재 Entity와 비교해 변경 사항이 있으면 UPDATE를 실행합니다.
@Transactional이 없는 메서드에서 Entity를 수정하면 더티 체킹이 동작하나요?일반적으로 동작하지 않습니다.
더티 체킹은 영속성 컨텍스트와 트랜잭션 안에서 의미가 있습니다.
트랜잭션 없이 Entity를 수정하면 변경 사항이 DB에 반영되지 않을 수 있습니다.
기본적으로 JPA는 전체 컬럼을 업데이트하는 경우가 많습니다.
변경된 컬럼만 업데이트하고 싶다면 Hibernate의 @DynamicUpdate를 사용할 수 있습니다.
다만 무조건 사용하는 것이 좋은 것은 아니고, 상황에 따라 선택해야 합니다.
A.
즉시 로딩은 Entity를 조회할 때 연관된 Entity까지 바로 조회합니다.
이로 인해 예상하지 못한 SQL이 실행되거나 성능 문제가 발생할 수 있습니다.
반면 지연 로딩은 실제로 연관 Entity가 필요할 때 조회하기 때문에 불필요한 조회를 줄일 수 있습니다.
그래서 실무에서는 기본적으로 LAZY를 권장합니다.
LazyInitializationException이 발생할 수 있습니다.
이는 영속성 컨텍스트가 닫힌 상태에서 프록시 객체를 초기화하려고 할 때 발생합니다.
N+1 문제는 처음에 1번의 쿼리로 목록을 조회한 뒤, 각 Entity의 연관 데이터를 조회하기 위해 추가 쿼리가 N번 발생하는 문제입니다.
Fetch Join을 사용하면 처음 조회할 때 연관 Entity를 함께 가져와 추가 쿼리 발생을 줄일 수 있습니다.
A.
세션 방식은 서버가 로그인 상태를 저장하고, 클라이언트는 Session ID를 쿠키로 전달합니다.
JWT 방식은 로그인 정보를 담은 토큰을 클라이언트가 가지고 있고, 서버는 토큰을 검증해서 인증합니다.
| 구분 | 세션 | JWT |
|---|---|---|
| 상태 관리 | 서버가 상태 저장 | 서버가 상태 저장하지 않음 |
| 확장성 | 세션 저장소 필요 | 상대적으로 확장 쉬움 |
| 무효화 | 쉬움 | 상대적으로 어려움 |
| 저장 위치 | 서버 | 클라이언트 |
JWT는 서버가 상태를 저장하지 않고 토큰 자체를 검증하는 방식입니다.
그래서 한 번 발급된 Access Token은 만료 시간 전까지 유효합니다.
즉시 무효화하려면 블랙리스트를 관리해야 하는데, 그러면 서버가 별도의 상태를 관리해야 합니다.
Access Token은 API 요청에 사용되기 때문에 탈취 위험을 줄이기 위해 만료 시간을 짧게 가져갑니다.
Refresh Token은 Access Token을 재발급받기 위해 사용하며, 상대적으로 긴 만료 시간을 가집니다.
이렇게 분리하면 보안성과 사용자 편의성을 함께 챙길 수 있습니다.
이번 면접 질문들은 Java 기본기부터 Spring, JPA, 인증 방식까지 백엔드 개발자가 반드시 이해해야 하는 핵심 개념들입니다.
단순히 키워드를 외우기보다는 다음 흐름으로 답변하는 연습이 중요합니다.
면접에서는 완벽한 암기보다, 개념을 실제 코드와 연결해서 설명하는 것이 중요합니다.

출처: 내일배움 스파르타 클럽