기술 면접 준비

[CS 모의면접 스터디] 스프링 편

Lea Hwang 2023. 11. 30. 02:04

23.11.30 CS 모의면접 스터디 <스프링 편>에서 사용할 질문&예상답변을 정리하였습니다. 

 

키워드
JPA(ORM), Hibernate, Spring Data JPA
JPA의 N+1문제
필터와 인터셉터
주요 어노테이션(@Component, @Service, @Repository, @Controller) 
Spring Security
WAS vs. WS
프로그래밍 패러다임(객체지향 프로그래밍, 절차형 프로그래밍, 함수형 프로그래밍..)
 

Q. JPA, Hibernate, Spring Data JPA 차이점에 대해 말씀해 주세요.

JPA는 Java에서 객체-관계 매핑(ORM)을 위한 표준 명세인 인터페이스입니다.

JPA는 Java Persistence API의 약자로, 자바 애플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스와 어노테이션의 표준 집합을 정의합니다.

여기서 주목할 부분은 JPA는 특정 기능을 하는 라이브러리가 아닌 인터페이스라는 점입니다.

자바 애플리케이션에서 관계형 데이터베이스를 어떻게 사용해야 하는지를 정의하는 한 방법일 뿐이고 단순한 명세이기에 구현은 없고, 다양한 ORM 프레임워크(예: Hibernate, EclipseLink, OpenJPA 등)에서 구현할 수 있는 공통 API를 제공합니다.

 

[꼬리질문] 그럼 Hibernate는 무엇인가요?

Hibernate는 JPA의 구현체입니다.

인터페이스를 직접 구현한 라이브러리입니다. JPA와 Hibernate 관계는 자바의 interface와 해당 interface를 구현한 class와 같은 관계입니다. Hibernate는 JPA의 모든 기능을 지원하며 Hibernate는 객체와 관계형 데이터베이스 간의 매핑을 자동으로 처리해서 개발자가 일일이 SQL 쿼리를 작성하지 않도록 도와줍니다.

Hibernate는 JPA의 구현체입니다. 즉, JPA를 사용하기 위해서 반드시 Hibernate'만'을 사용할 필요가 없다는 것입니다. Hibernate가 마음에 들지 않으면 다른 JPA 구현체를 사용할 수 있습니다.

 

[꼬리질문] Spring Data JPA에 대해서 말씀해 주세요.

Spring Data JPA는 JPA 기반 애플리케이션 개발을 보다 간편하게 만들기 위해 JPA위에 추가적인 기능과 추상화를 제공하는 라이브러리/프레임워크입니다.

Spring으로 개발하면서 EntityManager를 직접 다뤄본 적이 없었습니다. 그건  DB에 접근할 필요가 있는 대부분의 상황에서 Repository를 정의하여 사용했기 때문이었습니다. 이 Repository가 바로 Spring Data JPA의 핵심입니다.

Spring Data JPA는 Spring에서 제공하는 모듈 중 하나로, JPA 위에 추가적인 기능을 제공하여 JPA 기반 애플리케이션 개발을 보다 간편하게 만드는 라이브러리/프레임워크입니다.

사용자가 Repository 인터페이스에 정해진 규칙대로 메서드를 입력하면, Spring이 알아서 해당 메서드 이름에 적합한 쿼리를 날리는 구현체를 만들어서 Bean으로 등록해 줍니다.

CRUD 연산, 페이징, 정렬과 같은 JPA 리포지토리를 구현하는 데 필요한 반복적인 코드 양을 줄이는 인터페이스와 클래스를 제공합니다. Spring Data JPA는 또한 쿼리 메서드와 사용자 정의 쿼리를 지원하며, Spring 표현 언어(SpEL)와 유사한 구문을 사용하여 작성할 수 있습니다. 대표적인 예로는 .findAll() / .findById(Long id) / .save() / .delete()가 있습니다.

Q. JPA와 JPQL은 무슨 관계일까요?

JPQL은 Java Persistence Query Language의 약자로, DB 테이블이 아닌 엔티티의 객체를 대상으로 검색하는 객체 지향 쿼리입니다. SQL과 비슷한 문법을 가지고 있습니다.

JPQL을 사용하기 전까지는 EntityManger의 find를 통해 조회를 했는데, 조회가 복잡해지고 어려워질수록 사용하기가 까다로워집니다. 이를 해결하기 위해 JPQL이 나왔습니다.

JPA는 이러한 JPQL을 읽고 분석해서 SQL을 생성한 후 DB에 SQL을 실행하는 일을 합니다.

이덕분에 JPA는 특정 DB에 의존하지 않아 여러 DB에도 동일한 JPQL을 사용하면 DB에 접근할 수 있습니다. (Dialect - 방언만 변경하면 JPQL 수정 없이 DB 변경 가능)

 

[정리] 

JPA를 사용하기 위해 Repository 인터페이스를 만들고, JpaRepository를 상속받습니다. 여기에서 정의한 인터페이스 메서드를 실행하여 DB 연동을 하는데요. 이때, JPA는 인터페이스 메서드 이름을 분석해서 JPQL로 변환하고, 이를 가지고 SQL을 만들어서 DB에 SQL을 실행하는 과정을 합니다.

Q. JPA의 N+1문제란 무엇인지 그리고 언제 발생하는지 말씀해 주세요.

1번만 조회해야 할 것을 N개 종류의 데이터 각각을 추가로 조회하게 돼서 총 N+1번 DB조회를 하게 되는 문제입니다. 

이 문제는 일대다 또는 다대일 관계를 가진 엔티티를 조회할 때 발생합니다. 

JPA Fetch 전략(Fetch Type)이 EAGER 전략으로 데이터를 조회하는 경우 또는 JPA Fetch 전략(Fetch Type)이 LAZY 전략으로, 전체 데이터를 가져온 이후 연관 관계인 하위 엔티티를 사용할 때 다시 조회하는 경우에 발생합니다.

 

[꼬리질문] N+1문제의 해결방법과 주의점도 함께 말씀해 주세요.

해결 방법에는 여러 방법들이 있겠지만 대표적으로 FetchJoin과 EntityGraph 두 가지 방법이 있습니다. 

1. FetchJoin

JPQL에서 성능 개선 및 최적화를 위해 제공하는 기능으로 연관된 엔티티나 컬렉션을 함께 한 번에 조회하는 역할을 합니다. (Inner Join)

(일반 조인은 패치 조인과는 다르게 오직 지정된 SELECT 결과만 가져올 수 있고, 연관 엔티티나 컬렉션을 함께 가져오지 못합니다.)

별도 @Query를 활용해 SQL문 안에 join fetch 사용합니다. 

 

2. @Entity Graph

@EntityGraph의 attributePaths에 같이 조회할 연관 엔티티명을 적으면 됩니다. ,(콤마)를 통해 여러 개를 줄 수도 있습니다.Fetch join과 동일하게 JPQL을 사용해 Query문을 작성하고 필요한 연관관계를 EntityGraph에 설정하면 됩니다. (Outer join)

 

Fetch join의 경우 inner join을 하는 반면에 EntityGraph는 outer join을 기본으로 합니다. 

(기본적으로 outer join 보다 inner join이 성능 최적화에 더 유리.)

 

[꼬리질문] FetchJoin의 단점이 있다면 말씀해 주세요.

1. 번거롭게 쿼리문을 작성해야 함

2. JPA가 제공하는 Paging API 사용 불가능 (Pageable 사용 불가)

3. 한 엔티티 안에 일대다관계 컬렉션이 두 개 이상인 경우 사용 불가

4. 패치 조인 대상에게 별칭을 부여할 수 없음

 

[꼬리질문]  Fetch Join과 EntityGraph 사용 시 주의할 점이 있나요?

FetchJoin과 EntityGraph는 공통적으로 카테시안 곱(Cartesian Product)이 발생 하여 중복이 생길 수 있습니다.

※ 카테시안 곱 : 두 테이블 사이에 유효 join 조건을 적지 않았을 때 해당 테이블에 대한 모든 데이터를 전부 결합하여 테이블에 존재하는 행 갯수를 곱한만큼의 결과 값이 반환되는 것

 

이런 중복 발생 문제를 해결하기 위한 방법에는 두가지가 있습니다. 

1. JPQL에 DISTINCT 를 추가하여 중복 제거
2. OneToMany 필드 타입을 Set으로 선언하여 중복 제거

Q. 필터와 인터셉터의 공통점과 차이점에 대해 말씀해 주세요.

Filter, Interceptor는 모두 어떤 행동을 하기 전에 먼저 실행하거나, 실행한 후에 추가적인 행동을 할 때 사용됩니다.

 

Filter는 Dispatcher Servlet에 요청이 전달되기 전/후에 url 패턴에 맞는 모든 요청에 대한 작업을 처리할 수 있는 기능을 제공합니다.

 

스프링 컨테이너가 아닌  웹 컨테이너(톰캣)에 의해 관리되고, Request / Response 객체 조작이 가능합니다. 스프링과 무관하게 전역적으로 처리해야 하는 작업이나 웹 애플리케이션 전반적으로 사용 되는 기능을 구현합니다.

구체적으로는

- 공통된 보안 및 인증/인가 관련 작업(Spring Security)

- 모든 요청에 대한 로깅 또는 감사

- 이미지/데이터 압축 및 문자열 인코딩

 

반면, InterceptorSpring이 제공하는 기술로 Dispatcher Servlet이 컨트롤러를 호출하기 전과 후 끼어들어 스프링 컨텍스트 내부에서 Controller(Handler)에 관한 요청과 응답에 대해 처리합니다.

인터셉터는 스프링 컨테이너 내에서 동작하므로 필터를 거쳐 프런트 컨트롤러인 디스패처 서블릿이 요청을 받은 이후에 동작합니다. 스프링 컨테이너에 의해 관리되고, Request / Response객체 조작이 불가능합니다. 클라이언트의 요청과 관련된 전역적으로 처리해야 하는 작업이나 컨트롤러로 넘겨주기 위해 데이터 가공하는 작업을 합니다.

구체적으로

- 세부적인 보안 및 인증/인가 공통 작업

- API호출에 대한 로깅 또는 감사

- Controller로 넘겨주는 데이터 가공

Q. 주요 어노테이션에 대한 질문입니다.

[ @RequestParam과 @PathVariable 차이 ]

두 어노테이션은 Controller 단에서 uri를 통해 전달된 값을 파라미터로 받아오는 역할을 합니다.

이렇게 둘 다 데이터를 받아오는 데에 사용한다는 공통점이 있지만, 

@RequestParam는 쿼리 스트링 등을 이용해서 여러 데이터를 받아올 수 있고, @PathVariable은 값을 단 하나만 받아올 수 있습니다. 

 

[ @Controller와 @RestController 차이 ]

응답 방식에 대한 차이입니다. 

웹서버가 클라이언트한테 응답할 때는 .html형식인 File로 응답을 해주거나 문자열과 같은 데이터로 응답해 줄 수 있습니다. @Controller는 File을 응답하는 컨트롤러이고, @RestController는 Data를 응답하는 컨트롤러입니다.

@RestController = @Controller + @ResponseBody

(둘 중 하나만 사용 가능합니다.)

 

[ @Component, @Controller, @Service, @Repository ]

@Controller, @Service, @Repository 세 어노테이션은 @Component의 구체화된 형태로 해당 어노테이션이 사용된 곳은 @Component와 마찬가지로 자동으로 스프링 빈으로 등록됩니다.

1. 클라이언트가 서버에 페이지를 요청한다.
2. Dispatcher Servlet은 사용자의 요청에 알맞은 @Controller를 찾는다.
3. 만약 DB데이터를 이용한 처리 과정이 없다면, 단순히 View를 보여주면 여기서 끝이다.
4. 만약 DB가 필요하다면 @Controller는 알맞은 @Service로 가서 비즈니스 로직을 수행한다.
5. 수행하는 과정 중 DB를 접근하기 위하여 @Repository에 요청하여 DB로부터 필요한 값을 가져온다.
6. 순서대로 @Controller까지 결괏값을 가지고 return을 하게 되며, Model에 담은 다음에 View를 찾아 보여준다.

 

@Component

스프링은 과거 XML에만 빈 정의가 가능했지만, 스프링 2.5 버전부터는 어노테이션을 이용한 의존성 주입이 가능해졌습니다. @Component 가 사용된 클래스들은 스프링 빈으로 등록되고 자동으로 스프링 컨테이너가 해당 Bean을 관리하게 됩니다.

 

@Controller
@Controller라고 명시가 되면, 해당 클래스는 @Component에 의해 자동으로 Bean이 등록됩니다. 클라이언트가 특정 요청을 하게 되면 처리하기 위해서 스프링은 제일 먼저 해당하는 Controller를 찾게 되는데 이때 @Controller라고 명시된 클래스들을 탐색합니다. 그리고 난 다음에 Mapping주소가 일치하는 메서드의 내용을 실행하게 됩니다.

 

@Service

@Service라고 명시된 클래스는 비즈니스 로직에 대한 정보들이 담겨 있어야 합니다. 즉 사용자의 요청에 따라서 DB에 접근하여 데이터를 추가, 삭제, 수정, 선택과 같은 요청을 처리할 수 있어야 합니다.

 

@Repository

@Repository 어노테이션은 간단하게 DB 접근이 가능한 객체라고 생각하면 됩니다. 

 

[ Spring Bean 등록을 위한 어노테이션 @Bean, @Configuration, @Component 차이 및 비교 ]

기존의 Spring MVC에서는 xml을 활용하여 Bean을 등록을 했습니다. 하지만 프로젝트의 규모가 커짐에 따라 사용하는 요소들을 xml에 등록하는 것이 상당히 번거로워졌기에 어노테이션(Annotation, @)을 활용한 Bean 등록 방법이 탄생하게 되었습니다.

 

@Bean, @Configuration을 사용해서 수동으로 스프링 컨테이너에 빈을 등록할 수 있습니다. 

주로 개발자가 직접 제어가 불가능한 라이브러리를 빈으로 등록할 때 불가피하게 사용합니다. 그리고 유지보수성을 높이기 위해 애플리케이션 전범위적으로 사용되는 클래스나 다형성을 활용해 여러 구현체를 빈으로 등록할 때 사용합니다. 주의할 점은 @Bean은 반드시 @Configuration 안에서 사용되어야 하는데 그 이유는 프록시 패턴을 적용해 수동으로 등록하는 스프링 빈이 반드시 싱글톤으로 생성됨을 보장하기 위해서입니다.

 

@Component을 사용해서 자동으로 스프링 컨테이너에 빈을 등록할 수 있습니다. 

스프링은 기본적으로 컴포넌트 스캔을 이용한 자동 빈 등록 방식을 권장합니다. 스프링의 컴포넌트 스캔 기능이 @Component 어노테이션이 있는 클래스를 자동으로 찾아서 빈으로 등록합니다. 

@Component 하위 어노테이션으로는 @Configuration, @Controller, @Service, @Repository 등이 있습니다. 

Q. Spring Security는 무엇일까요?

Spring Security는 자바 애플리케이션에서 인증과 권한 부여, 일반적인 공격에 대한 보호 기능을 제공하는 스프링 하위 프레임워크입니다. 즉, 인증(Authentication, 누구인지?)과 인가(Authorization, 어떤 권한이 있는가?)를 담당하는 프레임워크를 말합니다.

 

Spring Security에서는 주로 서블릿 필터(filter)와 이들로 구성된 필터체인으로의 구성된 위임모델을 사용합니다. 그리고 보안과 관련해서 체계적으로 많은 옵션을 제공해 주기 때문에 개발자 입장에서는 일일이 보안 관련 로직을 작성하지 않아도 된다는 장점이 있습니다. 그리고 기본적으로 Spring Security는 세션 & 쿠키방식으로 인증합니다. 

 

[꼬리질문] Spring Security 적용과 무관하게 인증과 권한 부여의 흐름으로도 괜찮은 로그인 시스템을 구현할 수 있을 것 같은데 왜 Spring Security를 사용해야 할까요?

( Spring Security 적용과 무관하게 인증과 권한 부여의 흐름 )

1. 클라이언트가 크리덴션을 담아 인증 요청을 보낸다.

2. 인증관리자가 저장소에서 크리덴셜을 가져와 검증한다.

3. 인증 검증에 통과한 요청은 권한부여 관리자의 검증을 거쳐 통과한 사용자는 특정 리소스에 접근 권한을 획득한다.

Spring Security는 기존의 서블릿 필터와 쉽게 호환이 가능하며 필요한 거의 모든 추가 기능을 이미 구현해 두었기 때문입니다. 구체적으로 Session fixation(세션 ID 탈취), Clickjacking(클릭 가로채기), CSRF(위조 공격 전송)등에 대한 방어를 지원하고 모든 URL에 대해 인증을 요구하며 로그아웃 기능을 지원합니다. 그리고 HTTPS 접속을 강제합니다. 

 

[꼬리질문] Filter와 Security Filter의 차이는 무엇인가요?

Filter와 Security Filter 모두 Servlet에 요청이 매핑되기 전에 실행되는 필터(Filter)입니다. 둘 다 동일한 Filter이지만 단순한 Filter는 서블릿 컨테이너에 직접 등록해서 사용하는 필터이고 Security Filter는 DelegatingFilterProxy가 서블릿 컨테이너에 Filter로 등록되어서 Filter 작업을 Security FilterChain으로 위임해서 실행되는 필터를 의미합니다.

 

[꼬리질문] Spring Security의 전체적인 인증 프로세스를 설명해 주세요.

 

1. 사용자가 아이디 비밀번호로 로그인을 요청

2. AuthenticationFilter에서 UsernamePasswordAuthenticationToken을 생성하여 AuthenticationManager에게 전달

3. AuthenticationManager는 등록된 AuthenticationProvider(들)을 조회하여 인증을 요구함

4. AuthenticationProvider는 UserDetailsService를 통해 입력받은 아이디에 대한 사용자 정보를 DB에서 조회함

5. 입력받은 비밀번호를 암호화하여 DB의 비밀번호화 매칭되는 경우 인증이 성공된 UsernameAuthenticationToken을 생성해 AuthenticationManager로 반환함

6. AuthenticationManager는 UsernameAuthenticationToken을 AuthenticationFilter로 전달함

7. AuthenticationFilter는 전달받은 UsernameAuthenticationToken을 LoginSuccessHandler로 전송하고 SecurityContextHolder에 저장

Q. Web Server와 Web Application Server의 차이를 아시나요?

Web Server는 클라이언트가 요청한 정적인 콘텐츠인 HTML, CSS, Image 파일   WAS를 거치지 않고 바로 자원을 제공하거나, 동적인 컨텐츠 제공을 위한 요청을 받은 경우에는 클라이언트의 요청을 WAS에 보내고, WAS가 처리한 결과를 클라이언트에게 전달하는 역할을 합니다. 대표적인 예로는 Apache, WebtoB, Nginx, IIS 등이 있습니다. 


Web Application Server는 DB 연산이나 다양한 로직 처리를 요구하는 동적인 컨텐츠를 제공하기 위해 만들어졌습니다. Web Container의 JSP/Servlet 구동 환경을 제공하고 프로그래밍 언어(JSP, ASP, PHP 등)로 작성한 뒤 HTML 문서로 만들고 Web Server로 전달합니다. 대표적인 예로는 Tomcat, Jeus, JBoss 등이 있습니다. 


[꼬리질문] 그럼 Web Server와 Web Application Server가 필요한 이유가 무엇일까요?

Web Server에서는 정적 컨텐츠만 처리하도록 기능을 분배하여 Application Server까지 가지 않기에 서버의 부담을 줄일 수 있습니다.

WAS가 필요한 이유는 만약 Web Server만을 이용한다면 사용자가 원하는 요청에 대한 결괏값을 모두 미리 만들어 놓고 서비스를 해야 하므로 자원이 절대적으로 부족합니다. 

WAS를 통해 요청에 맞는 데이터를 DB에서 가져와서 비즈니스 로직에 맞게 그때그때 결과를 만들어서 제공함으로써 자원을 효율적으로 사용할 수 있습니다. 

 

[꼬리질문] 번거롭게 Web Server와 WAS 둘 다 사용하지 않고 WAS만 사용하면 되지 않을까요? 둘을 분리한 이유가 있을까요?

이는 Web Server를 WAS 앞에 둠으로써 얻는 이점을 살펴보면 이유를 알 수 있습니다.

 

1. 기능을 분리하여 서버 부하 방지

Web Server를 두는 가장 큰 이유입니다.  만약 정적 콘텐츠 요청까지 WAS가 처리한다면 정적 데이터 처리로 인해 부하가 커지게 되고, 동적 콘텐츠의 처리가 지연됨에 따라 수행 속도가 느려집니다.  그렇기에 단순한 정적 컨텐츠 요청은 Web Server에서 빠르게 클라이언트에 제공하여 WAS로 넘어오지 않게 막는 것이 좋습니다. 

 

2. 물리적으로 분리하여 보안 강화

WebServer와 WAS는 Port번호가 다릅니다. 이렇게 물리적으로 두 개의 서버를 완전히 분리하여 보안을 강화시켜 줄 수 있습니다. 또한 SSL에 대한 암복호화 처리에 Web Server를 사용하여 웹 서비스에 대한 보안을 강화시켜줄 수 있습니다.

 

3. 여러 대의 WAS를 연결 가능

Web Server하나에 여러 대의 WAS를 설치하고 Load Balancing을 하여 WAS의 부하를 더 낮춰줄 수도 있습니다. 

Q. 프로그래밍 패러다임에는 크게 명령형 프로그래밍과 선언형 프로그래밍으로 나눌 수 있습니다. 함수형 프로그래밍은 선언형 프로그래밍의 한 예인데요. 함수형 프로그래밍의 특징에 대해 알고 계시나요?

"부수효과(Side Effect)가 없는 순수 함수를 1급 객체로 간주하여 파라미터나 반환값으로 사용할 수 있으며, 참조 투명성을 지킬 수 있습니다. "

 

함수형 프로그래밍에서 함수는 1급 객체로 취급받기에 함수를 파라미터로 넘기거나 반환값으로 사용할 수 있습니다. 그리고 여기서 참조 투명성을 지킬 수 있다는 의미는 동일한 인자에 대해 항상 동일한 결과를 반환하여 실행 결과를 예측할 수 있다는 것을 의미합니다. 부작용을 제거하여 프로그램의 동작을 이해하고 예측을 용이하게 하는 것은 함수형 프로그래밍으로 개발하려는 핵심 동기 중 하나입니다.

* 순수 함수: 부수 효과를 제거한 함수로, 함수의 실행이 외부에 영향을 끼치지 않습니다. 함수 자체가 독립적이며 Side-Effect가 없기 때문에 Thread에 안전성을 보장받을 수 있습니다.


 

 

 

 

 

 

 


참고

[WEB] 🌐 웹 서비스 구조 (Web서버 / 웹컨테이너 / WAS) 정리

Spring Security 처리 과정

[Spring] 빈 등록을 위한 어노테이션 @Bean, @Configuration, @Component 차이 및 비교

[Spring] @Controller, @Service, @Repository 란? (feat.@Component)

[JPA] N+1 문제 원인 및 해결방법 알아보기