-
Notifications
You must be signed in to change notification settings - Fork 0
[Suhhee] week8 미션 #90
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Kimsuhhee04
wants to merge
7
commits into
Seohui
Choose a base branch
from
Suhhee-Week8
base: Seohui
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
c9ad2ea
feat: 내가 진행중인 미션 조회하기
Kimsuhhee04 5eac9aa
feat: 내가 생성한 리뷰들 조회하기
Kimsuhhee04 3161ba8
feat: Request Body가 있는 API에 검증 어노테이션 붙혀 검증하기
Kimsuhhee04 acb8305
docs: ch7 키워드
Kimsuhhee04 09ad777
Feat: Spring Security - Security 구조, 폼 로그인
Kimsuhhee04 819f272
Docs: ch08 키워드 추가
Kimsuhhee04 bc68e21
Refactor: 피드백 반영 코드 수정
Kimsuhhee04 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,246 @@ | ||
| # Page와 Slice | ||
|
|
||
| > **목적 / 용도** | ||
| > - **Page:** 게시판처럼 정확한 페이지 번호가 필요한 경우 | ||
| > - **Slice:** 무한 스크롤이나 더 보기 버튼이 필요한 경우 | ||
|
|
||
| | 구분 | Page | Slice | | ||
| | --- | --- | --- | | ||
| | **특징** | 전체 데이터 건수, 전체 페이지 수를 알 수 있음 | 전체 건수는 모르고 다음 페이지가 있는지의 여부만 알 수 있음 | | ||
| | **동작 방식** | 데이터 조회 쿼리 + 전체 개수를 세는 `COUNT` 쿼리가 같이 실행됨 | • `COUNT` 쿼리 실행 안 함<br>• 요청한 개수보다 **1개 더 (Limit + 1)** 가져와서 다음 페이지 여부 확인 | | ||
| | **성능** | 데이터가 많아질수록 `COUNT` 쿼리 때문에 성능이 저하될 수 있음 | `COUNT` 쿼리가 없어서 대용량 데이터 조회 시 성능이 훨씬 빠름 | | ||
| | **상속 구조** | `Slice` 인터페이스를 상속받음 (Slice의 모든 기능 + COUNT 기능) | 부모 인터페이스 | | ||
| | **UI** | [1] [2] [3] [4] [5][다음] | 스크롤을 맨 아래로 내리면 자동 로딩 | | ||
|
|
||
| **e.g.** | ||
| ```java | ||
| @Repository | ||
| public interface ReviewRepository extends JpaRepository<Review, Long> { | ||
|
|
||
| // Page 타입 반환 | ||
| Page<Review> findPageBy(Pageable pageable); | ||
|
|
||
| // Slice 타입 반환 | ||
| Slice<Review> findSliceBy(Pageable pageable); | ||
| } | ||
|
|
||
| @RestController | ||
| // ... | ||
| /** | ||
| Page 방식 | ||
| /reviews/page?page=0&size=5 | ||
| **/ | ||
| @GetMapping("/reviews/page") | ||
| public Page<Review> getReviewsByPage( | ||
| @RequestParam(defaultValue = "0") int page, | ||
| @RequestParam(defaultValue = "5") int size | ||
| ) { | ||
| // id 기준 최신순 정렬 Pageable 객체 생성 | ||
| Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "id")); | ||
|
|
||
| // 반환 시 전체 개수 정보가 응답됨 | ||
| return reviewRepository.findPageBy(pageable); | ||
| } | ||
|
|
||
| /** | ||
| Slice 방식 | ||
| /reviews/slice?page=0&size=5 | ||
| **/ | ||
| @GetMapping("/reviews/slice") | ||
| public Slice<Review> getReviewsBySlice( | ||
| @RequestParam(defaultValue = "0") int page, | ||
| @RequestParam(defaultValue = "5") int size | ||
| ) { | ||
| Pageable pageable = PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "id")); | ||
|
|
||
| // 전체 개수는 안 나옴 | ||
| // 데이터 내용, 다음 페이지 여부만 응답됨 | ||
| return reviewRepository.findSliceBy(pageable); | ||
| } | ||
| ``` | ||
|
|
||
| # Java Stream API | ||
|
|
||
| > **Stream API** | ||
| > 자바 8에 추가된 기능으로 컬렉션(List, Set, Map)이나 배열에 저장된 데이터를 반복문 없이 깔끔하게 처리하는 기술. | ||
| > 데이터의 흐름을 만듦 → 람다식과 함께 데이터를 필터링, 변환, 수집 작업을 연결해서 수행. | ||
|
|
||
| ### Stream API 동작 흐름 | ||
| 1. **생성(Creation)** → 컬렉션이나 배열을 스트림 객체로 만듦 (`.stream()`) | ||
| 2. **중간 연산(Intermediate)** → 데이터를 가공함 (여러 번 연결 가능) | ||
| 3. **최종 연산(Terminal)** → 가공된 데이터를 결과로 만듦 (한 번만 사용 가능하며, 이때 실제 연산이 시작됨) | ||
|
|
||
| --- | ||
|
|
||
| ### 중간 연산 | ||
|
|
||
| * **특징:** | ||
| * 데이터를 가공하는 단계 | ||
| * 연산 결과로 또 다른 `Stream`을 반환함 → 메서드 체이닝 가능 | ||
|
|
||
| | 메서드 | 설명 | 예시 / 비고 | | ||
| | --- | --- | --- | | ||
| | `filter(조건)` | 조건에 맞는 데이터만 걸러냄 | 짝수만 추출 | | ||
| | `map(변환 규칙)` | 데이터를 다른 형태로 변환 | User 객체에서 이름만 추출 | | ||
| | `sorted()` | 데이터 정렬 | 기본 오름차순, 사용자 지정 정렬 | | ||
| | `distinct()` | 중복된 데이터 제거 | | | ||
| | `limit(n)` | 데이터의 흐름에서 앞에서부터 n개의 데이터만 잘라냄 | | | ||
|
|
||
| --- | ||
|
|
||
| ### 최종 연산 | ||
|
|
||
| * **특징:** | ||
| * 가공된 데이터에서 최종 결과를 도출하는 마지막 단계 | ||
| * 실제 값이나 컬렉션을 반환하며, 최종 연산이 호출되어야 비로소 전체 스트림 연산이 실행됨 (지연 연산) | ||
| * 한 번 실행되면 스트림이 닫힘 | ||
|
|
||
| | 메서드 | 설명 | 예시 / 비고 | | ||
| | --- | --- | --- | | ||
| | `collect()` | 가공된 스트림 데이터를 List, Set, Map 등으로 묶어서 반환 | | | ||
| | `forEach()` | 스트림의 각 요소를 순회하며 출력하거나 작업을 수행함 | 반환값 없음 | | ||
| | `count()` | 스트림에 남은 데이터의 **총 개수** 반환 | | | ||
| | `reduce()` | 데이터를 하나씩 누적 계산 | 모든 숫자 합 계산 | | ||
| | `anyMatch(조건)` | 조건을 만족하는 데이터가 **하나라도 존재하는지** 확인 | boolean 반환 | | ||
|
|
||
| --- | ||
|
|
||
| ### 주의 사항 | ||
| * **스트림은 일회용임** → 한 번 최종 연산(`collect`, `count` 등)을 수행해서 결과를 얻으면 스트림은 닫혀서 다시 쓸 수 없다! 필요 시 재생성해야 함. | ||
| * **지연 연산(Lazy Evaluation)을 함** → 중간 연산(`filter`, `map`)을 많이 적어두어도 맨 끝에 최종 연산(`collect` 등)을 호출하지 않으면 계산이 아예 실행되지 않음. | ||
|
|
||
| # 객체 그래프 탐색 | ||
|
|
||
| > **객체 그래프 탐색** | ||
| > JPA나 ORM을 사용할 때 자주 등장하는 개념으로, 객체들 사이의 **연관 관계를 연쇄적으로 따라가며 조회하는 것** → 객체 안에 또 다른 객체가 있을 때 점(`.`)을 통해 타고 들어가면 된다. | ||
| > 실제 DB 조회 시점과 관련이 있기 때문에 중요함. | ||
| > 참조 변수를 통해 연관된 객체로 이동하는 행위를 말함. | ||
|
|
||
| **e.g.** | ||
| `comment.getUser().getName()` | ||
| * `comment`: 댓글 객체 | ||
| * `comment.getUser()`: 댓글을 작성한 사용자 (User) | ||
| * `comment.getUser().getName()`: 사용자의 이름 (Name) | ||
|
|
||
| --- | ||
|
|
||
| ### N+1 문제 | ||
| 객체 그래프 탐색은 필요할 때 가져온다는 지연 로딩(Lazy Loading) 특성을 가지기에 **N+1 문제**가 발생될 우려가 있음 → `fetch join`으로 해결 (한 번에 가져와서 객체 그래프 탐색 시 추가 쿼리 X) | ||
|
|
||
| * **현상:** 최초에 목록을 조회하는 쿼리를 한 번 날림 | ||
| * **문제:** 그 목록에 담긴 객체들을 하나씩 탐색할 때마다 연관된 데이터를 가져오기 위해 추가적인 쿼리가 N번 더 발생 | ||
| * **원인:** JPA 입장에서는 어디까지 탐색할지 미리 알 수 없음 → 처음에는 딱 요청한 것만 가져오고(프록시) 나중에 탐색을 시도할 때마다 쿼리를 날리기 때문에 발생하는 문제 | ||
|
|
||
| # @Valid vs @Validated | ||
|
|
||
| ### 차이점 | ||
|
|
||
| | 구분 | @Valid (자바 표준) | @Validated (스프링용) | | ||
| | --- | --- | --- | | ||
| | **소속** | Java/Jakarta EE 표준 규격(JSR-380) | Spring Framework | | ||
| | **동작 위치** | 주로 Controller의 파라미터 변환 시 동작 | Controller, Service, Repository 등 스프링 빈 어디서나 동작 | | ||
| | **동작 원리** | 스프링의 ArgumentResolver가 개입하여 검증 | 스프링의 AOP를 기반으로 메서드 호출을 가로채서 검증 | | ||
| | **그룹 지정** | 불가능 (무조건 전체 검증) | 가능 (상황에 따라 원하는 조건만 묶어서 검증) | | ||
| | **적용 대상** | 메서드 파라미터, 필드(객체 내부의 객체 검증 시) | 클래스 레벨, 메서드 파라미터 | | ||
| | **발생 예외 (Exception)** | `MethodArgumentNotValidException` | AOP 동작 시 `ConstraintViolationException` 발생 | | ||
|
|
||
| ### 공통점 | ||
| 객체에 설정된 제약 조건(`@NotNull`, `@Size` 등)을 확인하여 옳지 않은 데이터가 서버 내부로 들어오는 것을 막아줌 | ||
|
|
||
| --- | ||
|
|
||
| ## @Validated | ||
|
|
||
| > **@Validated** | ||
| > Spring 프레임워크에서 전용으로 제공하는 유효성 검증 어노테이션. | ||
| > 자바 표준인 `@Valid`의 모든 기능을 포함하면서, 추가적으로 **그룹화(Grouping)** 기능을 제공하여 특정 상황에 맞는 검증만 실행하라고 알려주는 신호. | ||
| > `@Valid`가 주로 웹(Controller) 계층에서만 동작한다면, `@Validated`는 스프링 빈으로 등록된 클래스라면 어디서든 유효성 검증이 가능해짐. | ||
|
|
||
| * **주 기능 : 그룹 유효성 검사 (Validation Groups)** | ||
| * ex) 회원 가입할 때와 회원 정보를 수정할 때 요구하는 데이터의 조건이 다를 경우 → 가입할 때는 비밀번호가 필수지만, 프로필 수정 시에는 비밀번호를 바꾸지 않을 수 있음 | ||
|
|
||
| ### @Validated 동작 흐름 | ||
| 1. 클라이언트 → Controller/Service 호출 | ||
| 2. Spring AOP 동작 (메서드 가로채기) | ||
| 3. 지정된 그룹 또는 파라미터 검증 | ||
| * **실패 :** `ConstraintViolationException` 또는 `MethodArgumentNotValidException` 발생 | ||
| * **성공 :** 대상 메서드의 비즈니스 로직 정상 수행 | ||
|
|
||
| ### 어노테이션 설명 (예시) | ||
| * `@NotBlank(groups = CreateGroup.class)` : 가입(Create)할 때만 필수 값으로 검증 | ||
| * `@NotNull(groups = UpdateGroup.class)` : 수정(Update)할 때만 null이 아닌지 검증 | ||
| * `@Validated(CreateGroup.class)` : 해당 그룹(Create)의 규칙만 검사하도록 지시함 | ||
|
|
||
| ### 클래스 레벨 검증 (Method Validation) | ||
| 클래스 위에 붙여 해당 클래스의 모든 메서드 파라미터에 대한 유효성 검사를 활성화할 수 있음 | ||
|
|
||
| ### 자바 @Valid와 비교했을 때 주의할 점 | ||
| `@Validated`를 클래스 레벨에 적용하여 발생하는 예외(`ConstraintViolationException`)와 컨트롤러 DTO 검증 시 발생하는 예외(`MethodArgumentNotValidException`)가 다름 | ||
| → 글로벌 예외 처리를 구현할 때 이 두 가지를 모두 처리해주어야 한다. | ||
|
|
||
| --- | ||
|
|
||
| ## @Valid | ||
|
|
||
| > **@Valid** | ||
| > Java Bean Validation의 기본 어노테이션. | ||
| > DTO, Entity, Method Parameter 등에서 검증을 시작하라고 Spring/Hibernate Validator에게 알려주는 신호. | ||
| > `@Valid`를 사용하면 객체 안에서 들어오는 값에 대해 검증이 가능해짐. | ||
|
|
||
| ### @Valid 동작 흐름 | ||
| 1. 클라이언트 → Controller 호출 | ||
| 2. Valid → Hibernate Validator 호출 | ||
| 3. DTO 필드 검증 | ||
| * **실패 :** `MethodArgumentNotValidException` 발생 | ||
| * **성공 :** Controller 비즈니스 로직 수행 | ||
|
|
||
| ### 검증 어노테이션 종류 | ||
|
|
||
| #### 문자열 검증 | ||
| | 어노테이션 | 설명 | | ||
| | --- | --- | | ||
| | `@NotBlank` | null X, 공백 제외 길이 > 0 | | ||
| | `@NotEmpty` | null X, 빈 문자열("") X | | ||
| | `@NotNull` | null X (타입 상관 없음) | | ||
| | `@Null` | 반드시 null 값 | | ||
|
|
||
| #### 최대/최솟값 검증 | ||
| | 어노테이션 | 설명 | | ||
| | --- | --- | | ||
| | `@DecimalMax` | 지정값 이하 (String 기반) | | ||
| | `@DecimalMin` | 지정값 이상 (String 기반) | | ||
| | `@Max` | 지정값 이하 (숫자 기반) | | ||
| | `@Min` | 지정값 이상 (숫자 기반) | | ||
|
|
||
| #### 범위 검증 | ||
| | 어노테이션 | 설명 | | ||
| | --- | --- | | ||
| | `@Positive` | 양수만 허용 | | ||
| | `@PositiveOrZero` | 0 이상 | | ||
| | `@Negative` | 음수만 허용 | | ||
| | `@NegativeOrZero` | 0 이하 | | ||
|
|
||
| *참고: `DecimalMax/Min`은 BigDecimal, BigInteger, String 등 정밀 수치용 / `Max/Min`은 기본 숫자형(int, long 등) 용* | ||
|
|
||
| #### 시간 값 검증 | ||
| | 어노테이션 | 설명 | | ||
| | --- | --- | | ||
| | `@Future` | 현재 시간보다 미래 | | ||
| | `@FutureOrPresent` | 현재 or 미래 | | ||
| | `@Past` | 현재 시간보다 과거 | | ||
| | `@PastOrPresent` | 현재 or 과거 | | ||
|
|
||
| #### Boolean 검증 | ||
| | 어노테이션 | 설명 | | ||
| | --- | --- | | ||
| | `@AssertTrue` | true 값만 허용 | | ||
| | `@AssertFalse` | false 값만 허용 | | ||
|
|
||
| #### 크기 검증 | ||
| | 어노테이션 | 설명 | | ||
| | --- | --- | | ||
| | `@Size` | 값의 길이가 min 이상 max 이하<br>→ `String`, `Collection`, `Map`, `Array` 에 적용 | | ||
|
|
||
| ### 자바 Valid 가 제공하지 않는 기능 | ||
| `@Valid`는 `javax.validation` 표준이지만, 그룹 유효성 검사를 지원하지 않음. | ||
| → 필요한 경우 `@Validated` 사용 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| # Spring Security가 무엇인가? | ||
|
|
||
| Java 서블릿 기반 애플리케이션에서 **인증(Authentication)**과 인가(Authorization), 보안 공격 방어 기능을 제공하는 프레임워크 | ||
| → 서블릿 필터의 체인 구조를 통해 요청을 가로채고 보안 로직을 처리함 | ||
| 요청이 DispatcherServlet(Controller)에 도달하기 전에 가로채서 보안 로직을 수행함 | ||
|
|
||
| * **DelegatingFilterProxy** | ||
| * 서블릿 컨테이너와 스프링 컨테이너(IoC 컨테이너)를 연결하는 역할을 하는 표준 서블릿 필터 | ||
| * 서블릿 필터는 스프링 빈을 직접 알지 못하므로 이 프록시 필터가 요청을 받아 스프링 빈으로 등록된 보안 필터(FilterChainProxy)에 위임 | ||
| * **FilterChainProxy** | ||
| * Spring Security의 실질적인 진입점으로 DelegatingFilterProxy로부터 요청을 받아 현재 요청에 적용되어야 할 SecurityFilterChain을 찾아 실행시키는 역할을 함 | ||
| * **SecurityFilterChain** | ||
| * 보안 처리를 위한 필터 묶음 | ||
| * SecurityConfig에서 `.authorizeHttpRequests()`, `.formLogin()` 등을 설정하면 이에 맞는 필터들이 체인 형태로 구성 | ||
|
|
||
| --- | ||
|
|
||
| # 인증(Authentication) vs 인가(Authorization) | ||
|
|
||
| ## 인증(Authentication) | ||
| 해당 사용자가 누구인지 확인하는 과정에서 필요한 주요 객체 | ||
|
|
||
| * **Authentication** | ||
| * 인증 전/ 인증 후의 사용자 정보를 담는 객체 | ||
| * 구성 요소: | ||
| * Principal: 사용자 식별자 (로그인 전: ID / 로그인 후: UserDetails 객체) | ||
| * Credentials: 자격 증명 (로그인 전: 비밀번호 / 로그인 후: 보안을 위해 삭제됨) | ||
| * Authorities: 사용자가 가진 권한 목록 (ROLE_USER, ROLE_ADMIN 등) | ||
| * **SecurityContext** | ||
| * 현재 인증된 사용자의 Authentication 객체를 보관하는 객체 | ||
| * **SecurityContextHolder** | ||
| * SecurityContext 저장하는 저장소 | ||
| * 기본적으로 ThreadLocal 전략으로 같은 쓰레드 내에서는 어디서든 인증 정보에 접근할 수 있음 | ||
| * 요청이 끝나면 초기화되지만 JWT 환경에서는 매 요청마다 필터가 이곳에 다시 값을 채워둠 | ||
| * **AuthenticationManager** | ||
| * 인증을 처리하는 최상위 인터페이스로 해당 인증 요청을 처리하라는 `authenticate()` 메서드 하나를 가지고있음 | ||
| * 실질적인 구현체는 대부분 ProviderManager 사용 | ||
| * **ProviderManager** | ||
| * AuthenticationManager의 구현체.. 스스로 인증을 하지 않고 여러 개의 AuthenticationProvider 목록을 순회하며 인증 처리를 위임 | ||
| * **AuthenticationProvider** | ||
| * 인증 로직을 수행하는 위치 | ||
| * `authenticate()`: 실제 검증 로직 수행 | ||
| * `supports()`: 해당 Provider가 특정 Authentication 타입을 처리할 수 있는지 확인 | ||
| * **UserDetailsService** | ||
| * DB나 메모리 등에서 유저 정보를 가져오는 역할 | ||
| * username으로 DB를 조회 → UserDetails 객체 반환 | ||
| * **UserDetails** | ||
| * Spring Security가 내부적으로 사용하는 유저 정보 표준 인터페이스 | ||
| * User 엔티티를 이 인터페이스에 맞춰 감싸거나(CustomUserDetails)/ 엔티티가 직접 구현하도록 하여 Spring Security에 전달 | ||
|
|
||
| ## 인가 (Authorization) | ||
| 해당 사용자가 이 리소스에 접근할 권한이 있는지 확인 | ||
|
|
||
| * **GrantedAuthority** | ||
| * 사용자 권한 | ||
| * SimpleGrantedAuthority 구현체 사용, ROLE_ADMIN 와 같은 문자열 형태로 저장 | ||
| * Authentication 객체 안에 리스트 형태로 저장 | ||
| * **AuthorizationFilter** | ||
| * 필터 체인의 마지막부에 위치하고 URL 별로 필요한 권한이 있는지 검사 | ||
| * SecurityConfig에서 작성한 `requestMatchers("/admin/**").hasRole("ADMIN")` 정보를 바탕으로 판단 | ||
|
|
||
| --- | ||
|
|
||
| # Stateful vs Stateless | ||
|
|
||
| * **Stateful (상태 유지)** | ||
| * 서버가 클라이언트와의 통신 상태나 이전 요청 데이터를 메모리나 디스크에 계속 저장하고 기억하는 네트워크 통신 방식 | ||
| * 클라이언트가 첫 번째 요청에서 인증을 완료하면 서버가 이를 기억하고 있으므로 두 번째 요청부터는 추가 인증 없이 작업을 처리할 수 있다 | ||
| * **Stateless (무상태)** | ||
| * 서버가 클라이언트의 상태를 전혀 저장하지 않는 네트워크 통신 방식임 | ||
| * 서버는 이전 요청이 무엇이었는지 전혀 알지 못하므로 클라이언트는 매 요청마다 서버가 작업을 처리하는 데 필요한 모든 데이터를 온전히 담아서 보내야 함 | ||
|
|
||
| | 구분 | Stateful (상태 유지) | Stateless (무상태) | | ||
| | :--- | :--- | :--- | | ||
| | **공통점** | 클라이언트와 서버 간의 네트워크 데이터 통신을 위한 설계 방식 | 클라이언트와 서버 간의 네트워크 데이터 통신을 위한 설계 방식 | | ||
| | **상태 저장** | 서버가 클라이언트의 이전 상태와 컨텍스트를 저장함 | 서버는 클라이언트의 상태를 저장하지 않음 | | ||
| | **요청 데이터** | 서버가 정보를 기억함 → 클라이언트는 최소한의 데이터만 전송 | 서버가 기억하지 않으므로 요청할 때마다 모든 데이터를 전송해야 함 | | ||
| | **서버** | 접속자가 많아질수록 서버 메모리 사용량이 급격히 증가 | 상태 저장을 하지 않음 → 서버 메모리 부담이 적음 | | ||
| | **확장성** | 여러 서버로 트래픽을 분산하는 Scale-out이 어려움 | 서버 간 상태 공유가 필요 없어 서버 확장에 자유로움 | | ||
| | **장애 대응** | 연결된 서버가 다운되면 해당 서버에 저장된 상태 정보가 날아감 → 다시 재작업 | 특정 서버가 다운되어도 다른 서버가 동일하게 요청을 처리할 수 있어 장애 대응에 유연 | | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stateful/Stateless 비교가 잘 정리되어 있습니다. 여기에 현재 구현한
formLogin은 기본적으로 세션 기반 Stateful 인증에 가깝고, JWT는 Stateless 인증 흐름에 가깝다는 식으로 코드 설정과 연결하면 학습 내용의 적용력이 더 높아집니다.