개발 공부/스프링

백엔드 계층 구조 정리 2

baby-t 2026. 3. 11. 21:35

6. Controller (프레젠테이션 계층)

  • 정의: 클라이언트(브라우저, 모바일 앱 등)의 HTTP 요청을 가장 먼저 맞이하는 입구. 요청 데이터를 검증하고, 알맞은 Service(주방)로 작업을 위임한 뒤 그 결과를 포장하여 응답하는 역할. (💡 실무 원칙: 컨트롤러 안에는 복잡한 비즈니스 로직이 들어가면 안 됨)
  • @Controller vs @RestController:
    • @Controller: 주로 서버에서 화면(HTML, Thymeleaf 등)을 직접 렌더링해서 페이지(View)의 경로를 반환할 때 사용. (SSR 방식)
    • @RestController: 화면 없이 **데이터(JSON)**만 반환하는 REST API 서버에서 사용. 내부적으로 @Controller와 JSON 변환을 지시하는 @ResponseBody가 합쳐진 형태. (CSR 방식)
  • 요청 데이터를 꺼내는 핵심 어노테이션:
    • @RequestBody: HTTP 본문(Body)에 담긴 JSON 데이터를 Request DTO로 변환하여 받음. (주로 POST, PUT)
    • @PathVariable: URL 경로 중간에 뚫려 있는 변수를 추출함. (예: /api/orders/{orderId})
    • @RequestParam: URL 끝에 물음표(?)로 붙어오는 쿼리 파라미터를 추출함. (예: 페이징 ?page=1&size=10)
  • 보안 정보의 우아한 활용 (@AuthenticationPrincipal):
    • Spring Security를 통과하여 SecurityContext(VIP 라운지)에 저장된 **현재 로그인 유저의 정보(UserDetails / PrincipalDetails)**를 파라미터로 즉시 주입받는 어노테이션.
    • 이 덕분에 토큰이나 세션에서 ID를 직접 파싱하거나 DB를 추가로 조회할 필요 없이, 즉시 유저의 식별자(ID)를 꺼내어 Service로 넘겨줄 수 있음.

7. REST API 설계 원칙 (Controller 내부)

  • 정의: 클라이언트와 서버가 HTTP 통신을 할 때, 누구나 목적을 쉽게 알아볼 수 있도록 자원(Resource)과 행위(Action)를 명확히 분리하여 통신하는 설계 방법론. 주로 JSON 형태로 데이터를 주고받음.
  • URI 명명 규칙 (국룰):
    • 자원을 표현할 때는 동사 대신 명사를 사용한다. (행위는 URI가 아니라 HTTP 메서드로 표현해야 함)
    • 단수형 대신 복수형을 사용한다.
    • (✅ 올바른 예: /api/users/1, ❌ 잘못된 예: /api/getUser/1)
  • HTTP 메서드 (행위의 분리):
    • GET : 데이터 조회 (Read)
    • POST : 데이터 생성 (Create)
    • PUT / PATCH : 데이터 수정 (Update - PUT은 전체 덮어쓰기, PATCH는 일부 수정)
    • DELETE : 데이터 삭제 (Delete)
  • 스프링 컨트롤러 매핑 어노테이션:
    • @RequestMapping: 클래스 단에 붙여 해당 컨트롤러의 공통 URI 경로를 지정함. (예: @RequestMapping("/api/orders"))
    • @GetMapping, @PostMapping 등: 각 메서드에 붙여 HTTP 요청 방식에 따라 알맞은 로직으로 길을 안내(라우팅)함.
    • @ResponseBody: 서버가 반환하는 자바 객체를 JSON 형식으로 변환해 응답함. (단, 클래스에 @RestController를 붙이면 모든 메서드에 이 기능이 자동 적용되므로 생략 가능)

8. 추가 핵심 기술: 리플렉션 (Reflection)

  • 정의: 구체적인 클래스 타입을 알지 못하더라도, 클래스의 이름만 알면 그 클래스의 내부 정보(메서드, 필드, 어노테이션 등)에 접근하고 객체를 강제로 생성할 수 있게 해주는 자바(Java)의 기본 API (java.lang.reflect).
  • 작동 시점 (가장 중요 ⭐): 코드를 컴파일하는 '빌드 시점'이 아니라, 프로그램이 실제로 실행 중인 **'런타임(Runtime)'**에 동적으로 작동함.
  • 스프링 부트 & JPA에서의 활용:
    • 스프링 빈 생성자: 스프링이 켜질 때, 리플렉션을 통해 모든 클래스를 스캔하여 @Controller, @Service 등이 붙은 클래스를 찾아냄. 이후 런타임에 알아서 객체(Bean)를 생성하고 의존성을 주입해 줌.
    • JPA 엔티티 생성: JPA가 DB에서 가져온 데이터를 자바 객체로 만들 때, 리플렉션을 사용해 기본 생성자(@NoArgsConstructor)로 빈 껍데기 객체를 먼저 만든 뒤 private 필드에 값을 직접 꽂아 넣음.

9. 설정 파일들 (프로젝트의 뼈대와 제어판)

  • build.gradle (의존성 관리): 프로젝트 빌드 시 필요한 외부 라이브러리(기술)들을 추가하고 관리하는 파일. (implementation으로 추가하면 전 세계 저장소에서 알아서 다운로드해 줌)
  • application.yml (통합 제어 패널): 프로젝트의 모든 환경 변수를 통제하는 곳. DB 연결 정보, JPA 설정(ddl-auto), 포트 번호 등을 설정함. (로컬용과 운영 서버용을 분리하는 Profile 기능도 여기서 관리)
  • @Configuration (자바 기반 설정): .yml 파일의 텍스트만으로는 부족할 때, 자바 코드를 통해 복잡한 객체(Bean)를 조립해서 스프링에 미리 세팅해 두는 클래스. (Security, WebClient, 비동기 설정 등)
  • .gitignore (보안 투명 망토): Git(GitHub)에 코드를 올릴 때 절대 올라가면 안 되는 파일들의 목록을 적어두는 곳. (DB 비밀번호, JWT 시크릿 키 등이 담긴 설정 파일이나 빌드 결과물을 누출로부터 방어)

10. Spring Security와 UserDetails

  • 정의: 스프링 기반 애플리케이션의 인증(Authentication, 누구인지 확인)과 인가(Authorization, 권한이 있는지 확인)를 전담하는 강력한 보안 프레임워크.
  • 전통적인 인증 처리 아키텍처 (흐름):
    1. 클라이언트의 HTTP 요청이 들어오면 Filter(AuthenticationFilter 등)가 이를 가로챔.
    2. 필터는 검증을 **AuthenticationManager**에게 위임하고, 매니저는 다시 실제 검증을 수행할 **AuthenticationProvider**에게 넘김.
    3. Provider는 **UserDetailsService**를 호출하여 DB에서 실제 유저 정보를 조회하고 대조함.
    4. 인증이 성공하면 이 유저의 최종 인증 객체(Authentication)를 서버의 VIP 라운지인 **SecurityContextHolder**에 저장함.
  • 로그인 유지 방식의 두 갈래:
    • Form Login (세션 기반): 서버가 메모리(방명록)에 유저의 세션을 저장하고, 클라이언트(쿠키)에게는 세션 ID만 발급하는 전통적 방식.
    • JWT (토큰 기반): 서버는 상태를 저장하지 않고(Stateless - 기억상실증), 유저의 데이터와 서버의 '위조 방지 서명'이 담긴 토큰을 발급. 클라이언트가 이 토큰을 보관하며, 요청 시 서버는 서명만 대조하여 검증함.
  • SecurityConfig의 역할: 전체 보안 교통정리. CSRF 공격 방어 설정(켜기/끄기), URL 경로별 접근 권한(누구나 접근 가능한 permitAll vs 로그인해야 하는 authenticated), 로그인 페이지 등록 등을 통제함.
  • UserDetails와 컨트롤러 연동 (@AuthenticationPrincipal):
    • UserDetails: 스프링 시큐리티가 이해할 수 있는 유저 정보의 '표준 규격'.
    • PrincipalDetails: 우리의 실제 유저 엔티티를 이 표준 규격에 맞게 감싸는 일종의 포장지(Adapter).
    • 인증 과정을 통과해 이 포장지가 SecurityContextHolder에 안착하면, Controller에서는 @AuthenticationPrincipal 어노테이션 하나만으로 굳이 DB를 다시 뒤지지 않고도 로그인한 유저의 정보를 즉시 꺼내서 사용할 수 있음.