패스잇

백엔드 개발

Java / Spring 면접 질문

Spring의 DI/IoC, AOP, 트랜잭션, 빈 생명주기 — 백엔드 자바 면접의 핵심을 모범답안과 함께 정리했습니다. 프레임워크를 "써 봤다"를 넘어 내부 동작과 설계 의도를 설명할 수 있어야 합니다.

총 21문제 · 기초 7 · 중급 7 · 심화 7 · 모범답안 포함

Java / Spring 면접 질문 — 기초

Q1 기초

Spring의 IoC(Inversion of Control)와 DI(Dependency Injection)의 개념과 장점에 대해 설명해주세요.

힌트 · 객체 생성과 의존성 관리의 책임이 어디에 있는지 생각해보세요.

IoC(제어의 역전)는 객체의 생성 및 생명 주기 관리에 대한 제어 권한이 개발자가 아닌 컨테이너에게 넘어가는 것을 의미합니다. 기존에는 개발자가 직접 객체를 생성하고 관리했지만, IoC 컨테이너가 이를 대신 처리해…

전체 모범답안 펼치기

IoC(제어의 역전)는 객체의 생성 및 생명 주기 관리에 대한 제어 권한이 개발자가 아닌 컨테이너에게 넘어가는 것을 의미합니다. 기존에는 개발자가 직접 객체를 생성하고 관리했지만, IoC 컨테이너가 이를 대신 처리해주는 것이죠.

DI(의존성 주입)는 IoC를 구현하는 구체적인 방법 중 하나입니다. 객체가 필요로 하는 의존성을 직접 생성하는 대신, 컨테이너로부터 주입받는 방식으로 구현됩니다.

IoC/DI를 사용하면 다음과 같은 장점이 있습니다.

  • 결합도 감소: 객체 간의 의존성이 줄어들어 코드 변경에 따른 영향이 최소화됩니다.
  • 유연성 증가: 객체 간의 관계가 느슨해져 코드 재사용성이 높아지고, 다양한 환경에 쉽게 적용할 수 있습니다.
  • 테스트 용이성: 의존성을 쉽게 Mock 객체로 대체할 수 있어 단위 테스트가 용이해집니다.

예를 들어, OrderServiceProductRepository에 의존한다면, 직접 new ProductRepository()를 호출하는 대신, Spring 컨테이너가 ProductRepository 인스턴스를 OrderService에 주입해주는 방식입니다.

#제어의 역전#의존성 주입#결합도 감소#유연성 증가#테스트 용이성

이 질문 단독 페이지 →

Q2 기초

@Component, @Service, @Repository, @Controller의 차이점은 무엇인가요?

힌트 · 각 애노테이션의 역할과 계층 구조를 생각해보세요.

@Component, @Service, @Repository, @Controller는 모두 Spring 프레임워크에서 빈(Bean)을 등록하기 위해 사용하는 애노테이션입니다. 핵심적인 차이는 각 애노테이션이 나타내는…

전체 모범답안 펼치기

@Component, @Service, @Repository, @Controller는 모두 Spring 프레임워크에서 빈(Bean)을 등록하기 위해 사용하는 애노테이션입니다. 핵심적인 차이는 각 애노테이션이 나타내는 역할과 관점에 있습니다.

@Component는 가장 일반적인 애노테이션으로, 특정 역할을 명확히 정의하기 어려운 빈에 사용됩니다.

@Service는 비즈니스 로직을 수행하는 클래스에 사용되며, 서비스 계층을 나타냅니다.

@Repository는 데이터 접근 로직을 수행하는 클래스에 사용되며, 데이터베이스와의 상호작용을 담당합니다. 또한, 데이터 접근 예외를 추상화하여 예외 처리의 일관성을 유지하는 데 도움을 줍니다.

@Controller는 웹 요청을 처리하고 응답을 반환하는 클래스에 사용되며, 프레젠테이션 계층을 나타냅니다.

이 애노테이션들은 @Component를 상속받아 만들어졌기 때문에, 빈 등록 기능은 동일하지만, 각 계층의 역할을 명확히 구분하고 AOP(Aspect-Oriented Programming)와 같은 기술을 적용할 때 더욱 효과적으로 사용할 수 있도록 도와줍니다. 즉, 개발자가 코드의 목적과 역할을 더 명확하게 파악할 수 있도록 돕는 역할을 합니다.

#역할#관점#계층#책임#AOP

이 질문 단독 페이지 →

Q3 기초

Java의 Optional 클래스는 왜 만들어졌고, 어떻게 사용하나요?

힌트 · NullPointerException을 방지하는 방법을 생각해보세요.

Java의 Optional 클래스는 NullPointerException을 효과적으로 방지하기 위해 도입되었습니다. 기존에는 null 체크를 일일이 해야 했지만, Optional을 사용하면 값이 없을 수도 있는 상황…

전체 모범답안 펼치기

Java의 Optional 클래스는 NullPointerException을 효과적으로 방지하기 위해 도입되었습니다. 기존에는 null 체크를 일일이 해야 했지만, Optional을 사용하면 값이 없을 수도 있는 상황을 명시적으로 표현하고 더 안전하게 처리할 수 있습니다.

Optional은 값을 감싸는 래퍼 클래스입니다. 값이 존재하면 그 값을 가지고, 값이 없으면 비어있는 Optional 객체를 가집니다.

주요 사용법은 다음과 같습니다. Optional.of(value)로 값을 감싸거나, 값이 null일 가능성이 있다면 Optional.ofNullable(value)을 사용합니다. 값이 있는지 확인하려면 isPresent() 메서드를 사용하고, 값이 없을 경우 기본값을 제공하려면 orElse() 메서드를 활용합니다. 예를 들어 Optional<String> name = Optional.ofNullable(getName()); String result = name.orElse("Unknown"); 처럼 사용할 수 있습니다.

Optional을 사용하면 코드 가독성이 높아지고, null 관련 오류를 컴파일 시점에 어느 정도 방지할 수 있다는 장점이 있습니다.

#NullPointerException#null-safe#isPresent()#orElse()#Optional 객체

이 질문 단독 페이지 →

Q4 기초

Java의 equals()와 hashCode() 메서드를 왜 함께 오버라이드해야 하나요?

힌트 · HashMap, HashSet 등 해시 기반 컬렉션에서의 동작을 생각해보세요.

Java에서 equals()와 hashCode() 메서드를 함께 오버라이드해야 하는 이유는 주로 해시 기반 컬렉션(HashMap, HashSet 등)에서 객체를 올바르게 사용하기 위해서입니다.

전체 모범답안 펼치기

Java에서 equals()hashCode() 메서드를 함께 오버라이드해야 하는 이유는 주로 해시 기반 컬렉션(HashMap, HashSet 등)에서 객체를 올바르게 사용하기 위해서입니다.

equals() 메서드는 객체의 내용이 같은지 비교하는 역할을 합니다. 반면 hashCode() 메서드는 객체의 해시 코드를 반환합니다.

만약 equals()를 오버라이드했는데 hashCode()를 오버라이드하지 않으면, equals()로 비교했을 때 같은 객체라도 hashCode()는 다른 값을 반환할 수 있습니다. 해시 기반 컬렉션은 객체의 해시 코드를 사용하여 객체를 저장하고 검색하기 때문에, 이 경우 동일한 객체가 컬렉션 내에서 다르게 취급될 수 있습니다.

equals()가 true를 반환하는 두 객체는 반드시 동일한 hashCode() 값을 가져야 한다는 규칙이 있습니다. 이 규칙을 지키지 않으면 해시 기반 컬렉션에서 예상치 못한 동작이 발생할 수 있습니다. 따라서 equals()를 오버라이드할 때는 반드시 hashCode()도 함께 오버라이드하여 객체의 동등성을 일관성 있게 유지해야 합니다.

#equals 계약#hashCode 계약#동등성 비교#해시 테이블#컬렉션 프레임워크

이 질문 단독 페이지 →

Q5 기초

Java의 Checked Exception과 Unchecked Exception의 차이점과 사용 지침에 대해 설명해주세요.

힌트 · 복구 가능성과 프로그래밍 오류 관점에서 생각해보세요.

Checked Exception과 Unchecked Exception은 Java 예외 처리의 두 가지 주요 유형입니다. 가장 큰 차이점은 컴파일 시점에 예외 처리 여부를 확인하는지 여부입니다.

전체 모범답안 펼치기

Checked Exception과 Unchecked Exception은 Java 예외 처리의 두 가지 주요 유형입니다. 가장 큰 차이점은 컴파일 시점에 예외 처리 여부를 확인하는지 여부입니다.

Checked Exception은 Exception 클래스를 상속받으며, 반드시 try-catch 블록으로 처리하거나 throws 구문을 사용하여 메서드에 던져야 합니다. 컴파일러가 이를 강제하므로, 개발자는 예외 발생 가능성을 인지하고 대비해야 합니다. 주로 복구 가능한 상황, 예를 들어 파일이 존재하지 않는 경우 등에 사용됩니다.

Unchecked Exception은 RuntimeException 클래스를 상속받으며, 컴파일러가 예외 처리를 강제하지 않습니다. 즉, try-catch 블록으로 감싸지 않아도 컴파일 에러가 발생하지 않습니다. 주로 프로그래밍 오류, 예를 들어 NullPointerException이나 ArrayIndexOutOfBoundsException과 같은 상황에서 발생합니다. 이러한 예외는 일반적으로 프로그램 로직의 결함으로 인해 발생하므로, 예외 처리보다는 코드 수정으로 해결하는 것이 좋습니다.

Checked Exception은 예외 발생 가능성이 높고 복구 가능한 상황에, Unchecked Exception은 프로그래밍 오류로 인해 발생하며 복구 가능성이 낮은 상황에 사용하는 것이 일반적인 지침입니다.

#컴파일_시점#RuntimeException#예외_처리_강제#예외_처리_선택#복구_가능성

이 질문 단독 페이지 →

Q6 기초

Spring의 프로파일(Profile) 기능은 무엇이며, 어떻게 활용하나요?

힌트 · 환경별 설정 분리와 @Profile 애노테이션 사용을 생각해보세요.

Spring 프로파일은 애플리케이션을 다양한 환경(개발, 운영, 테스트 등)에 맞춰 구성할 수 있도록 해주는 기능입니다. 예를 들어, 개발 환경에서는 로컬 DB를 사용하고 운영 환경에서는 실제 DB를 사용하도록 설정…

전체 모범답안 펼치기

Spring 프로파일은 애플리케이션을 다양한 환경(개발, 운영, 테스트 등)에 맞춰 구성할 수 있도록 해주는 기능입니다. 예를 들어, 개발 환경에서는 로컬 DB를 사용하고 운영 환경에서는 실제 DB를 사용하도록 설정할 수 있습니다.

@Profile 애노테이션을 사용해서 특정 프로파일에서만 활성화되는 빈을 정의할 수 있습니다. 그리고 application.properties 또는 application.yml 파일에서 spring.profiles.active 속성을 설정하여 활성화할 프로파일을 지정합니다.

이렇게 하면 환경별로 필요한 빈들만 로드하여 애플리케이션을 실행할 수 있어서, 코드 변경 없이 배포 환경에 따라 유연하게 설정할 수 있습니다.

#환경 구성#빈 설정#활성화#application.properties#@Profile

이 질문 단독 페이지 →

Q7 기초

Java의 String, StringBuilder, StringBuffer의 차이점은 무엇인가요?

힌트 · 불변성, 동기화, 성능 측면에서 비교해보세요.

네, Java에서 String, StringBuilder, StringBuffer는 문자열을 다루는 클래스이지만 중요한 차이점이 있습니다.

전체 모범답안 펼치기

네, Java에서 String, StringBuilder, StringBuffer는 문자열을 다루는 클래스이지만 중요한 차이점이 있습니다.

가장 큰 차이점은 불변성입니다. String은 불변 클래스이기 때문에 한 번 생성된 String 객체의 내용은 변경할 수 없습니다. 문자열을 수정하는 연산을 수행하면 새로운 String 객체가 생성됩니다.

반면 StringBuilder와 StringBuffer는 가변 클래스입니다. 즉, 객체의 내용을 직접 수정할 수 있습니다.

StringBuilder와 StringBuffer의 차이점은 동기화 여부입니다. StringBuffer는 동기화를 지원하므로 멀티 스레드 환경에서 안전하게 사용할 수 있습니다. 반면 StringBuilder는 동기화를 지원하지 않기 때문에 단일 스레드 환경에서 더 빠른 성능을 보입니다.

따라서, 문자열 내용이 자주 변경되는 경우 StringBuilder (단일 스레드) 또는 StringBuffer (멀티 스레드)를 사용하는 것이 String을 사용하는 것보다 성능상 유리합니다.

#불변성#가변성#동기화#스레드 안전#성능

이 질문 단독 페이지 →

읽기만으론 부족합니다 — 직접 말해보세요

패스잇 앱에서 Java / Spring 질문에 직접 답하면 AI가 1:1로 답변을 코칭합니다.

Java / Spring 면접 질문 — 중급

Q8 중급

Spring의 @Transactional 애노테이션의 propagation 속성에 대해 설명하고, REQUIRED와 REQUIRES_NEW의 차이점을 설명해주세요.

힌트 · 트랜잭션 전파 방식과 각각의 사용 사례를 생각해보세요.

@Transactional 애노테이션의 propagation 속성은 트랜잭션 경계 내에서 다른 트랜잭션이 호출될 때 어떻게 동작할지를 결정합니다. 즉, 기존 트랜잭션이 있을 때 새로운 트랜잭션을 어떻게 처리할지 정의…

전체 모범답안 펼치기

@Transactional 애노테이션의 propagation 속성은 트랜잭션 경계 내에서 다른 트랜잭션이 호출될 때 어떻게 동작할지를 결정합니다. 즉, 기존 트랜잭션이 있을 때 새로운 트랜잭션을 어떻게 처리할지 정의하는 것이죠.

REQUIRED는 기존 트랜잭션이 있으면 참여하고, 없으면 새로운 트랜잭션을 시작합니다. 가장 일반적인 설정이며, 대부분의 경우에 적합합니다.

REQUIRES_NEW는 항상 새로운 트랜잭션을 시작합니다. 기존 트랜잭션이 있더라도 잠시 중단하고 새로운 물리적인 트랜잭션을 시작합니다. 예를 들어, 핵심 로직의 트랜잭션 실패와 관계없이 항상 로그를 기록해야 하는 경우에 유용합니다. 로그 기록 실패가 핵심 로직 트랜잭션에 영향을 주지 않도록 분리하는 것이죠. REQUIRED는 논리적인 트랜잭션으로 묶이지만, REQUIRES_NEW는 물리적으로 분리된 트랜잭션을 사용합니다.

#트랜잭션 전파#REQUIRED#REQUIRES_NEW#물리적 트랜잭션#논리적 트랜잭션

이 질문 단독 페이지 →

Q9 중급

Spring Bean의 생명주기(Lifecycle)에 대해 설명해주세요. 초기화와 소멸 콜백은 어떻게 처리하나요?

힌트 · @PostConstruct, @PreDestroy, InitializingBean 등을 생각해보세요.

Spring Bean의 생명주기는 Bean이 생성되고, 사용 가능하게 되며, 소멸되는 과정을 말합니다.

전체 모범답안 펼치기

Spring Bean의 생명주기는 Bean이 생성되고, 사용 가능하게 되며, 소멸되는 과정을 말합니다.

Bean이 생성된 후 초기화 단계에서는 InitializingBean 인터페이스의 afterPropertiesSet 메서드나 @PostConstruct 어노테이션을 사용하여 Bean 초기화 작업을 수행할 수 있습니다. InitializingBean은 인터페이스를 구현해야 하지만, @PostConstruct는 어노테이션만 붙이면 되므로 더 간편하게 사용할 수 있습니다.

Bean이 소멸되기 전에는 DisposableBean 인터페이스의 destroy 메서드나 @PreDestroy 어노테이션을 사용하여 Bean 소멸 작업을 수행합니다. 마찬가지로 @PreDestroy가 더 편리합니다.

BeanPostProcessor를 사용하면 Bean의 초기화 전후에 사용자 정의 로직을 추가할 수도 있습니다. 이는 Bean의 생성 과정에 더 세밀한 제어를 가능하게 합니다.

#BeanPostProcessor#InitializingBean#DisposableBean#@PostConstruct#@PreDestroy

이 질문 단독 페이지 →

Q10 중급

Spring에서 AOP(Aspect Oriented Programming)란 무엇이며, 어떤 상황에서 사용하나요?

힌트 · 횡단 관심사(Cross-cutting concerns)를 생각해보세요.

Spring AOP는 Aspect Oriented Programming의 약자로, 핵심 비즈니스 로직에서 횡단 관심사를 분리하여 코드 중복을 줄이고 유지보수성을 높이는 프로그래밍 패러다임입니다.

전체 모범답안 펼치기

Spring AOP는 Aspect Oriented Programming의 약자로, 핵심 비즈니스 로직에서 횡단 관심사를 분리하여 코드 중복을 줄이고 유지보수성을 높이는 프로그래밍 패러다임입니다.

횡단 관심사란 로깅, 보안, 트랜잭션 관리처럼 여러 메서드에 공통적으로 적용되는 기능을 의미합니다. AOP를 사용하면 이러한 횡단 관심사를 Aspect라는 모듈로 분리하고, 핵심 로직을 수정하지 않고도 필요한 시점에 적용할 수 있습니다.

주로 로깅, 권한 검사, 트랜잭션 관리, 성능 모니터링 등에 사용되며, 코드를 깔끔하게 유지하고 변경 사항을 쉽게 적용할 수 있도록 해줍니다. 예를 들어, 모든 API 호출에 대한 로깅을 AOP로 구현하면, 각 API 메서드에 로깅 코드를 직접 추가하지 않아도 됩니다.

#관점#횡단 관심사#Advice#JoinPoint#Proxy

이 질문 단독 페이지 →

Q11 중급

Spring Boot의 자동 설정(Auto Configuration)은 어떻게 동작하나요?

힌트 · @EnableAutoConfiguration과 spring.factories를 생각해보세요.

Spring Boot의 자동 설정은 개발자가 직접 Bean을 등록하지 않아도 Spring 애플리케이션을 쉽게 구성할 수 있도록 도와주는 기능입니다. 핵심은 @EnableAutoConfiguration 어노테이션과 s…

전체 모범답안 펼치기

Spring Boot의 자동 설정은 개발자가 직접 Bean을 등록하지 않아도 Spring 애플리케이션을 쉽게 구성할 수 있도록 도와주는 기능입니다. 핵심은 @EnableAutoConfiguration 어노테이션과 spring.factories 파일입니다.

@EnableAutoConfiguration 어노테이션은 애플리케이션 시작 시 자동 설정 클래스들을 로드하도록 지시합니다. 이 때, Spring Boot는 META-INF/spring.factories 파일에서 자동 설정 클래스 목록을 찾습니다.

spring.factories 파일에는 org.springframework.boot.autoconfigure.EnableAutoConfiguration 키에 해당하는 자동 설정 클래스들이 나열되어 있습니다. 각 자동 설정 클래스는 @ConditionalOnClass와 같은 조건부 어노테이션을 사용하여 특정 클래스가 classpath에 존재할 때만 Bean을 등록하도록 구성됩니다. 예를 들어, DataSource 클래스가 존재하면 데이터베이스 관련 Bean을 자동으로 등록하는 식입니다.

이러한 자동 설정은 Spring Boot Starter를 통해 제공되며, 개발자는 필요한 Starter만 추가하여 애플리케이션에 필요한 기능들을 쉽게 활성화할 수 있습니다.

#@EnableAutoConfiguration#ConditionalOnClass#META-INF/spring.factories#starter#Bean 등록

이 질문 단독 페이지 →

Q12 중급

Java 8의 Stream API와 병렬 스트림(Parallel Stream)의 사용법과 주의사항에 대해 설명해주세요.

힌트 · 내부 반복, 지연 연산, 병렬 처리 시 주의점을 생각해보세요.

Java 8의 Stream API는 데이터 컬렉션을 효율적으로 처리하기 위한 기능입니다. 내부 반복을 사용하여 코드를 간결하게 만들고, 지연 연산을 통해 불필요한 연산을 줄여 성능을 향상시킬 수 있습니다.

전체 모범답안 펼치기

Java 8의 Stream API는 데이터 컬렉션을 효율적으로 처리하기 위한 기능입니다. 내부 반복을 사용하여 코드를 간결하게 만들고, 지연 연산을 통해 불필요한 연산을 줄여 성능을 향상시킬 수 있습니다.

병렬 스트림은 여러 스레드를 사용하여 스트림 연산을 병렬로 처리합니다. parallel() 메서드를 호출하여 간단하게 사용할 수 있지만, 몇 가지 주의사항이 있습니다.

첫째, 공유 자원에 대한 동기화 문제가 발생할 수 있습니다. 여러 스레드가 동시에 공유 자원에 접근하면 데이터 불일치 문제가 발생할 수 있으므로, 동기화 처리가 필요합니다.

둘째, 항상 성능 향상을 보장하는 것은 아닙니다. 데이터 크기가 작거나 연산 자체가 간단한 경우에는 오히려 스레드 생성 및 관리 비용 때문에 성능이 저하될 수 있습니다. ForkJoinPool을 사용하는데, 스레드 풀 관리 비용이 발생합니다.

따라서 병렬 스트림을 사용하기 전에 성능 측정을 통해 실제로 성능 향상이 있는지 확인하는 것이 중요합니다.

#Stream API#병렬 스트림#ForkJoinPool#공유 자원#성능 측정

이 질문 단독 페이지 →

Q13 중급

Spring Security의 인증(Authentication)과 인가(Authorization)의 차이점과 동작 방식을 설명해주세요.

힌트 · 필터 체인과 SecurityContext를 생각해보세요.

Spring Security에서 인증(Authentication)과 인가(Authorization)는 보안의 핵심적인 두 가지 단계입니다.

전체 모범답안 펼치기

Spring Security에서 인증(Authentication)과 인가(Authorization)는 보안의 핵심적인 두 가지 단계입니다.

인증은 '누구'인지 확인하는 과정입니다. 사용자가 아이디와 비밀번호를 입력하면, Spring Security는 이 정보를 기반으로 사용자를 식별하고, Principal 객체를 생성하여 SecurityContext에 저장합니다. 이 Principal 객체는 이후 인가 과정에서 사용됩니다.

반면, 인가는 인증된 사용자가 '무엇'을 할 수 있는지 결정하는 과정입니다. 인증된 사용자가 특정 리소스에 접근하려고 할 때, Spring Security는 사용자의 권한(Authorities)을 확인하여 접근 권한을 부여하거나 거부합니다. 예를 들어, 'ADMIN' 권한을 가진 사용자만 특정 API를 호출할 수 있도록 설정할 수 있습니다.

Spring Security의 필터 체인을 통해 이 두 과정이 순차적으로 처리됩니다. 인증 필터가 먼저 사용자를 인증하고, 인가 필터가 인증된 사용자의 권한을 확인하여 접근 제어를 수행합니다.

#인증#인가#Authentication#Authorization#Principal

이 질문 단독 페이지 →

Q14 중급

JWT(JSON Web Token)의 구조와 Spring에서의 활용 방법에 대해 설명해주세요.

힌트 · Header, Payload, Signature 구조와 검증 방식을 설명해보세요.

JWT는 웹 표준 인증 방식 중 하나로, Header, Payload, Signature 세 부분으로 구성됩니다.

전체 모범답안 펼치기

JWT는 웹 표준 인증 방식 중 하나로, Header, Payload, Signature 세 부분으로 구성됩니다.

Header는 토큰 타입과 암호화 알고리즘 정보를 담고, Payload는 사용자 정보나 권한 같은 클레임 정보를 JSON 형태로 담습니다. Signature는 Header와 Payload를 특정 알고리즘으로 암호화한 값으로, 토큰의 유효성을 검증하는 데 사용됩니다.

Spring에서는 Spring Security와 함께 JWT를 많이 사용합니다. 인증 과정에서 JWT를 생성하여 클라이언트에게 전달하고, 클라이언트는 요청 시 JWT를 Header에 담아 보냅니다. 서버는 Interceptor나 Filter를 통해 JWT를 검증하고, 유효한 경우에만 요청을 처리합니다. Spring Security JWT 라이브러리를 사용하면 JWT 생성, 검증 로직을 쉽게 구현할 수 있습니다.

#Header#Payload#Signature#Spring Security#Interceptor

이 질문 단독 페이지 →

Java / Spring 면접 질문 — 심화

Q15 심화

Spring에서 싱글톤 빈의 동시성 문제는 어떻게 해결하나요?

힌트 · 상태를 가지지 않는 설계, ThreadLocal 등을 고려해보세요.

Spring의 싱글톤 빈은 애플리케이션 내에서 단 하나의 인스턴스만 생성되므로, 여러 스레드가 동시에 접근할 때 동시성 문제가 발생할 수 있습니다.

전체 모범답안 펼치기

Spring의 싱글톤 빈은 애플리케이션 내에서 단 하나의 인스턴스만 생성되므로, 여러 스레드가 동시에 접근할 때 동시성 문제가 발생할 수 있습니다.

가장 좋은 해결책은 싱글톤 빈이 상태를 가지지 않도록 설계하는 것입니다. 즉, 멤버 변수를 최소화하고, 필요한 경우 불변 객체를 사용하거나, 로컬 변수만을 활용하는 것이죠.

만약 불가피하게 상태를 가져야 한다면, ThreadLocal을 사용하여 각 스레드마다 독립적인 상태를 유지하도록 할 수 있습니다. 또는 synchronized 키워드나 Atomic 변수를 사용하여 동기화를 처리할 수도 있지만, 성능 저하를 고려해야 합니다.

// ThreadLocal 예시
private ThreadLocal<String> myContext = new ThreadLocal<>();

public void process(String input) {
    myContext.set(input);
    // ... 작업 수행 ...
    myContext.remove(); // 사용 후 반드시 제거
}

핵심은 싱글톤 빈의 상태를 최소화하고, 동시성 문제가 발생할 수 있는 부분을 명확히 파악하여 적절한 동기화 메커니즘을 적용하는 것입니다.

#ThreadLocal#synchronized#Atomic#불변객체#상태_관리_회피

이 질문 단독 페이지 →

Q16 심화

Java의 Garbage Collection 동작 원리와 G1 GC의 특징에 대해 설명해주세요.

힌트 · 힙 메모리 구조와 GC 알고리즘을 설명해보세요.

Java의 Garbage Collection은 더 이상 사용하지 않는 객체를 자동으로 메모리에서 해제하여 메모리 누수를 방지하는 기능입니다. 힙 메모리는 크게 Young Generation과 Old Generatio…

전체 모범답안 펼치기

Java의 Garbage Collection은 더 이상 사용하지 않는 객체를 자동으로 메모리에서 해제하여 메모리 누수를 방지하는 기능입니다. 힙 메모리는 크게 Young Generation과 Old Generation으로 나뉘는데, Young Generation은 Eden, Survivor 0, Survivor 1 영역으로 구성됩니다.

GC는 Mark and Sweep 알고리즘을 기반으로 동작합니다. Mark 단계에서는 사용 중인 객체를 식별하고, Sweep 단계에서는 사용하지 않는 객체를 제거합니다. Young Generation에서 발생하는 GC를 Minor GC, Old Generation에서 발생하는 GC를 Major GC라고 합니다. GC 실행 시에는 STW (Stop-The-World)가 발생하여 잠시 애플리케이션 실행이 멈추게 됩니다.

G1 GC는 JDK 7부터 도입된 GC 알고리즘으로, 힙 메모리를 Region이라는 작은 단위로 나누어 관리합니다. 이를 통해 STW 시간을 줄이고, 예측 가능한 GC 성능을 제공하는 것이 특징입니다. G1 GC는 각 Region의 가비지 양을 추적하여 회수할 Region을 우선순위에 따라 선택적으로 처리합니다.

#Mark and Sweep#Young Generation#Old Generation#Region#STW (Stop-The-World)

이 질문 단독 페이지 →

Q17 심화

Java의 synchronized 키워드와 ReentrantLock의 차이점은 무엇인가요?

힌트 · 락 획득 방식, 공정성, 조건 변수 등을 비교해보세요.

면접관님, synchronized 키워드와 ReentrantLock은 Java에서 동기화를 구현하는 대표적인 방법입니다. 둘 다 여러 스레드가 공유 자원에 동시에 접근하는 것을 막아주지만, 몇 가지 중요한 차이점이…

전체 모범답안 펼치기

면접관님, synchronized 키워드와 ReentrantLock은 Java에서 동기화를 구현하는 대표적인 방법입니다. 둘 다 여러 스레드가 공유 자원에 동시에 접근하는 것을 막아주지만, 몇 가지 중요한 차이점이 있습니다.

가장 큰 차이점은 락 획득 방식입니다. synchronized는 암묵적인 락을 사용하며, 락 획득 및 해제가 자동으로 이루어집니다. 반면 ReentrantLock은 명시적인 락을 사용하며, lock()unlock() 메서드를 통해 락을 획득하고 해제해야 합니다.

또한, ReentrantLocksynchronized보다 더 많은 기능을 제공합니다. 예를 들어, ReentrantLock은 공정성(fairness)을 설정할 수 있어 락을 기다리는 스레드에게 공정한 순서로 락을 할당할 수 있습니다. synchronized는 기본적으로 비공정 락입니다.

조건 변수(Condition Variable)를 사용할 수 있다는 점도 중요한 차이점입니다. ReentrantLockCondition 객체를 통해 스레드를 대기시키고 깨우는 기능을 제공하여, 복잡한 동기화 로직을 구현하는 데 유용합니다.

마지막으로, ReentrantLock은 락 획득 시도를 중단할 수 있는 tryLock() 메서드를 제공하여, 데드락 회피에 유용하게 사용될 수 있습니다. synchronized는 락 획득 시도를 중단할 수 없습니다.

요약하자면, synchronized는 간단하고 사용하기 쉽지만, ReentrantLock은 더 많은 기능과 유연성을 제공합니다. 상황에 따라 적절한 동기화 방법을 선택하는 것이 중요합니다.

#synchronized#ReentrantLock#블로킹#공정성#유연성

이 질문 단독 페이지 →

Q18 심화

Spring WebFlux와 기존 Spring MVC의 차이점은 무엇인가요?

힌트 · 블로킹 vs 논블로킹, Mono/Flux 개념을 생각해보세요.

Spring WebFlux와 Spring MVC의 가장 큰 차이점은 동작 방식입니다. Spring MVC는 Servlet 기반의 동기/블로킹 방식인 반면, Spring WebFlux는 Reactor 기반의 비동기/N…

전체 모범답안 펼치기

Spring WebFlux와 Spring MVC의 가장 큰 차이점은 동작 방식입니다. Spring MVC는 Servlet 기반의 동기/블로킹 방식인 반면, Spring WebFlux는 Reactor 기반의 비동기/Non-Blocking 방식으로 동작합니다.

Spring MVC에서는 요청을 처리하는 스레드가 I/O 작업이 완료될 때까지 대기하지만, WebFlux에서는 I/O 작업이 완료될 때까지 스레드가 다른 작업을 수행할 수 있습니다. 이를 통해 WebFlux는 더 적은 스레드로 더 많은 요청을 처리할 수 있어, 동시성 처리 성능이 향상됩니다.

WebFlux는 Mono와 Flux라는 Reactive Streams API를 사용하여 데이터를 비동기적으로 처리합니다. Mono는 0 또는 1개의 데이터를, Flux는 0개 이상의 데이터를 나타냅니다. 또한 Backpressure를 지원하여 생산자가 소비자의 처리 속도에 맞춰 데이터를 전송하도록 제어할 수 있습니다.

결론적으로, Spring WebFlux는 Non-Blocking I/O와 Reactive Streams를 통해 더 높은 확장성과 응답성을 제공하는 데 초점을 맞춘 프레임워크입니다.

#비동기#Non-Blocking#Reactor#Backpressure#Servlet Stack

이 질문 단독 페이지 →

Q19 심화

Java의 volatile 키워드의 역할과 사용 시 주의사항에 대해 설명해주세요.

힌트 · 가시성(visibility) 보장과 원자성(atomicity)의 차이를 생각해보세요.

Java의 volatile 키워드는 주로 멀티스레드 환경에서 변수의 가시성(visibility)을 보장하는 데 사용됩니다. 즉, 한 스레드에서 변경된 volatile 변수의 값이 다른 모든 스레드에 즉시 보이도록 합…

전체 모범답안 펼치기

Java의 volatile 키워드는 주로 멀티스레드 환경에서 변수의 **가시성(visibility)**을 보장하는 데 사용됩니다. 즉, 한 스레드에서 변경된 volatile 변수의 값이 다른 모든 스레드에 즉시 보이도록 합니다. 이는 CPU 캐시와 메인 메모리 간의 일관성을 유지하기 위해 컴파일러와 JVM에게 특별한 지시를 내리는 것과 같습니다. volatile은 변수 읽기/쓰기 작업 시 **메모리 장벽(memory barrier)**을 생성하여, 이전 작업이 완료된 후에 다음 작업이 수행되도록 강제합니다.

하지만 volatile은 **원자성(atomicity)**을 보장하지 않습니다. 예를 들어, volatile int count 변수에 대해 count++ 연산을 수행할 때, 이는 읽기, 증가, 쓰기 세 단계로 나뉘므로 원자적이지 않습니다. 따라서 여러 스레드가 동시에 count++를 실행하면 예상치 못한 결과가 발생할 수 있습니다. volatile은 단일 변수에 대한 읽기/쓰기 작업의 가시성을 보장하지만, 복잡한 연산의 원자성을 보장하려면 synchronized 블록이나 java.util.concurrent 패키지의 원자적 변수 클래스를 사용해야 합니다. volatilehappens-before 관계를 통해 스레드 간의 순서를 보장하는 데에도 기여합니다.

#가시성#메모리 장벽#원자성#캐시 일관성#happens-before

이 질문 단독 페이지 →

Q20 심화

MSA 환경에서 여러 서비스에 걸친 분산 트랜잭션을 구현해야 할 때, Spring 환경에서 어떤 접근 방식(예: 2PC, Saga 패턴)을 고려할 수 있으며, 각 방식의 장단점과 실제 구현 시 고려사항은 무엇인지 설명해 주세요.

힌트 · 2PC는 동기적이고 복잡하며, Saga는 비동기적이고 보상 트랜잭션을 포함합니다. 각 패턴의 일관성 모델과 복구 전략을 비교하여 설명하세요.

MSA 환경에서 분산 트랜잭션 처리는 중요한 과제입니다. Spring 환경에서는 주로 두 가지 접근 방식을 고려할 수 있습니다.

전체 모범답안 펼치기

MSA 환경에서 분산 트랜잭션 처리는 중요한 과제입니다. Spring 환경에서는 주로 두 가지 접근 방식을 고려할 수 있습니다.

첫째, 2PC(Two-Phase Commit) 패턴입니다. 이는 전통적인 ACID 트랜잭션의 원자성을 분산 환경으로 확장하는 방식입니다. 트랜잭션 코디네이터가 참여 서비스들에게 커밋 또는 롤백 명령을 내리는 2단계로 진행됩니다.

  • 장점: 강력한 일관성(Strong Consistency)을 보장합니다. 모든 참여자가 성공해야만 커밋되므로 데이터 불일치 위험이 적습니다.
  • 단점: 동기적이고 블로킹 방식이라 성능 저하가 발생할 수 있습니다. 코디네이터나 참여 서비스 장애 시 복구가 복잡하며, 서비스 간 결합도가 높아집니다. Spring에서는 JTA(Java Transaction API) 등을 통해 구현할 수 있습니다.

둘째, Saga 패턴입니다. 이는 각 서비스를 독립적인 로컬 트랜잭션으로 처리하고, 실패 시 이전 트랜잭션을 보상하는 방식으로 분산 트랜잭션을 관리합니다.

  • 장점: 비동기적이고 느슨한 결합을 지원하여 확장성과 가용성이 높습니다. 각 서비스는 독립적으로 운영될 수 있습니다.
  • 단점: 최종 일관성(Eventual Consistency)을 제공합니다. 보상 트랜잭션 구현이 복잡하며, 멱등성(Idempotency) 처리가 필수적입니다. Spring에서는 Kafka, RabbitMQ와 같은 메시징 시스템을 활용하거나 Spring Cloud Sleuth, Spring Cloud Stream 등을 통해 구현할 수 있습니다.

실제 구현 시에는 서비스의 특성, 요구되는 일관성 수준, 성능 요구사항 등을 종합적으로 고려하여 적합한 패턴을 선택해야 합니다. 2PC는 강력한 일관성이 필수적인 경우에, Saga는 높은 가용성과 확장성이 중요한 경우에 적합합니다.

#2PC#Saga 패턴#분산 트랜잭션#ACID#멱등성

이 질문 단독 페이지 →

Q21 심화

Spring Security를 사용하여 OAuth 2.0/OIDC 기반의 인증/인가 시스템을 구축할 때, 토큰 발급 및 검증 과정에서 JWT를 활용하는 경우의 장점과 보안 고려사항(예: 토큰 탈취, 리플레이 어택 방지)은 무엇이며, 이를 Spring Security에서 어떻게 구현할 수 있는지 구체적으로 설명해 주세요.

힌트 · JWT의 무상태성, 서명/암호화, Access Token/Refresh Token 전략, 그리고 Spring Security의 필터 체인 및 커스텀 AuthenticationProvider를 활용한 구현 방안을 언급하세요.

Spring Security에서 JWT를 OAuth 2.0/OIDC 인증/인가에 활용하는 것은 여러 장점을 가집니다.

전체 모범답안 펼치기

Spring Security에서 JWT를 OAuth 2.0/OIDC 인증/인가에 활용하는 것은 여러 장점을 가집니다.

JWT의 가장 큰 장점은 무상태성입니다. 서버는 사용자 정보를 세션에 저장할 필요 없이 JWT 자체만으로 인증 및 인가 정보를 확인할 수 있어 확장성이 뛰어납니다. 또한, JWT는 서명(JWS) 또는 **암호화(JWE)**를 통해 무결성과 기밀성을 보장할 수 있습니다.

보안 측면에서는 몇 가지 고려사항이 있습니다.

  • 토큰 탈취: JWT가 탈취되면 공격자가 해당 토큰으로 사용자 행세를 할 수 있습니다. 이를 방지하기 위해 토큰에 민감한 정보를 담지 않고, 짧은 만료 시간을 설정하며, HTTPS를 통해 통신해야 합니다. 또한, Refresh Token 전략을 사용하여 Access Token의 만료 시간을 짧게 유지하고, Refresh Token은 안전한 저장소에 보관하여 Access Token 재발급에 사용합니다.
  • 리플레이 공격: 탈취된 토큰이 재사용되는 리플레이 공격을 막기 위해, JWT에 exp (만료 시간) 클레임을 반드시 포함하고, 서버에서는 이 만료 시간을 검증해야 합니다. 더 나아가, jti (JWT ID) 클레임을 사용하여 고유 식별자를 부여하고, 서버에서 발급된 토큰 ID를 추적하여 중복 사용을 방지할 수도 있습니다.

Spring Security에서는 이러한 JWT 기반 인증/인가를 필터 체인 내에서 구현합니다.

  1. JWT 발급: OAuth 2.0/OIDC 인증 플로우가 성공하면, Spring Security는 사용자 정보를 기반으로 JWT를 생성합니다. 이때 Secret Key를 사용하여 서명하고, 필요한 클레임(예: sub, roles, exp)을 포함시킵니다.
  2. JWT 검증: 클라이언트로부터 요청이 오면, Spring Security의 OncePerRequestFilter와 같은 커스텀 필터가 요청 헤더에서 JWT를 추출합니다. 추출된 JWT는 Secret Key로 서명을 검증하고, 만료 시간(exp)을 확인합니다.
  3. Authentication 객체 생성: 검증이 성공하면, JWT의 클레임 정보를 바탕으로 UsernamePasswordAuthenticationToken과 같은 Authentication 객체를 생성합니다. 이 객체에는 사용자 정보와 권한이 포함됩니다.
  4. SecurityContextHolder: 생성된 Authentication 객체는 SecurityContextHolder에 저장되어, 이후 요청 처리 과정에서 인증 및 인가 정보를 활용할 수 있게 됩니다.

이 과정에서 JwtDecoderJwtValidator를 설정하여 JWT의 유효성을 체계적으로 검증하며, CustomAuthenticationProvider를 통해 검증된 JWT 정보를 Authentication 객체로 변환하는 로직을 구현할 수 있습니다. Refresh Token 관리를 위해서는 별도의 TokenService를 구현하여 안전한 발급 및 검증 로직을 추가합니다.

#JWT#JWS/JWE#OAuth 2.0/OIDC#Spring Security#토큰 탈취/리플레이 공격 방지

이 질문 단독 페이지 →

함께 보면 좋은 백엔드 개발 면접 질문

← 전체 면접 질문 카테고리 보기

보유한 Java / Spring 질문은 이게 전부가 아닙니다

패스잇 앱에는 직무별 면접 질문 수천 개와 모범답안이 담겨 있습니다. AI 모의면접으로 직접 답하고, 약점을 분석받아 보세요.