👍 무엇을 새롭게 알았는지
Spring에서 Bean의 개념에 대한 학습을 진행
- 같은 타입의 Bean이 두개일 때 발생하는 현상을 확인하기
- food라는 패키지 내에 Food(interface), Chicken(class), Pizza(class)를 생성하여 연결하였음
- 연결되는 부분은 같은 Food 타입으로 연결되는 상황
// Food.java (interface)
package com.sparta.springauth.food;
public interface Food {
void eat();
}
// Chicken.java (class)
package com.sparta.springauth.food;
import org.springframework.stereotype.Component;
@Component
public class Chicken implements Food {
@Override
public void eat() {
System.out.println("치킨을 먹습니다.");
}
}
package com.sparta.springauth.food;
import org.springframework.stereotype.Component;
@Component
public class Pizza implements Food {
@Override
public void eat() {
System.out.println("피자를 먹습니다.");
}
}
- 두 클래스 모두 @Component 를 통해 Bean으로 등록되어 있으며 같은 타입으로 연결되어 있어도 오류는 발생하지 않음
- 해당 부분을 테스트하기 위해 BeenTest.java 파일을 생성
// BeanTest.java
package com.sparta.springauth;
import com.sparta.springauth.food.Food;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class BeenTest {
@Autowired
Food food;
@Test
@DisplayName("테스트")
void test1() {
food.eat();
}
}
- 이렇게 연결된 상황에서는 @Autowired로 Food 타입을 주입받으려 할 때 오류가 발생
- 어떤 Bean을 받아야 할 지 이해하지 못해 명시가 필요함
- 이를 해결하기 위해서는 3가지의 해결방법이 있음
1. Bean 이름 명시하기
package com.sparta.springauth;
import com.sparta.springauth.food.Food;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class BeenTest {
@Autowired
Food chicken;
@Autowired
Food pizza;
@Test
@DisplayName("테스트")
void test1() {
chicken.eat();
pizza.eat();
}
}
- 이렇게 각 Bean의 이름을 명시하여 활용하면 정상적으로 구현체가 연결되었기 때문에 정상적으로 코드 구성이 완료됨
2. @Primary 사용하기
package com.sparta.springauth.food;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
@Component
@Primary
public class Chicken implements Food {
@Override
public void eat() {
System.out.println("치킨을 먹습니다.");
}
}
package com.sparta.springauth;
import com.sparta.springauth.food.Food;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class BeenTest {
@Autowired
Food food;
@Test
@DisplayName("Primary 와 Qualifier 우선순위 확인")
void test1() {
food.eat();
}
}
- @Primary 로 어노테이션을 받은 Chicken 클래스가 우선적으로 주입되어 출력되는 것을 확인할 수 있음
3. @Qualifier 사용하기
package com.sparta.springauth.food;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
@Qualifier("pizza")
public class Pizza implements Food {
@Override
public void eat() {
System.out.println("피자를 먹습니다.");
}
}
package com.sparta.springauth;
import com.sparta.springauth.food.Food;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class BeenTest {
@Autowired
@Qualifier("pizza")
Food food;
@Test
@DisplayName("Primary 와 Qualifier 우선순위 확인")
void test1() {
food.eat();
}
}
- @Qualifier를 통해 Food 타입 주입 시 피자가 주입됨
- 단 지정한 이름이 정확하게 일치하여야 오류발생하지 않음
→ 이를 통해 @Qualifier가 @Primary 보다 우선순위를 가지게 되고, 범용적으로 사용되는 Bean의 경우 @Primary를, 특정 상황에서 사용되는 Bean의 경우 @Qualifier를 사용하는 것을 권장함
→ 중복 설정 시 좁은 범위(@Qualifier)가 큰 범위(@Primary)보다 먼저 사용됨을 잊지 말 것
인증(Authentication) 과 인가(Authorization)
- 인증
- 인증은 해당 유저가 실제 유저인지 인증하는 개념
- 인가
- 해당 유저가 특정 리소스에 접근이 가능한지 허가를 확인하는 개념 (Ex. 관리자 권한)
쿠키와 세션
- 쿠키
- 클라이언트에 저장될 목적으로 생성한 작은 정보를 담은 파일
- 구성 요소
- Name (이름) : 쿠키를 구별하는데 사용되는 키 (중복될 수 없음)
- Value (값) : 쿠키의 값
- Domain (도메인) : 쿠키가 저장된 도메인
- Path (경로) : 쿠키가 사용되는 경로
- Expires (만료기한) : 쿠키의 만료기한 (만료기한이 지나면 삭제됨)
- 세션
- 서버에서 일정시간 동안 클라이언트 상태를 유지하기 위해 사용됨
- 서버에서 클라이언트 별로 유일무이한 '세션 ID'를 부여한 후 클라이언트 별 필요한 정보를 서버에 저장
- 서버에서 생성한 '세션 ID'는 클라이언트의 쿠키값('세션 쿠키' 라고 부름)으로 저장되어 클라이언트 식별에 사용됨
| 항목 | 쿠키 (Cookie) | 세션 (Session) |
| 설명 | 클라이언트에 저장될 목적으로 생성한 작은 정보를 담은 파일 | 서버에서 일정시간 동안 클라이언트 상태를 유지하기 위해 사용 |
| 저장 위치 | 클라이언트 (웹 브라우저) | 웹 서버 |
| 사용 예 | 사이트 팝업의 "오늘 다시보지 않기" 정보 저장 | 로그인 정보 저장 |
| 만료 시점 | 쿠키 저장 시 만료일시 설정 가능 (브라우저 종료시도 유지 가능) |
다음 조건 중 하나가 만족될 경우 만료됨 1. 브라우저 종료 시까지 2. 클라이언트 로그아웃 시까지 3. 서버에 설정한 유지기간까지 해당 클라이언트의 재요청이 없는 경우 |
| 용량 제한 | 브라우저 별로 다름 (크롬 기준) - 하나의 도메인 당 180개 - 하나의 쿠키 당 4KB(=4096byte) |
개수 제한 없음 (단, 세션 저장소 크기 이상 저장 불가능) |
| 보안 | 취약 (클라이언트에서 쿠키 정보를 쉽게 변경, 삭제 및 가로채기 당할 수 있음) |
비교적 안전 (서버에 저장되기 때문에 상대적으로 안전) |
JWT란?
: JWT(Json Web Token)란 JSON 포맷을 이용하여 사용자에 대한 속성을 저장하는 Claim 기반의 Web Token. 즉 토큰의 한 종류
JWT 사용 이유
- 서버가 1대인 경우

- Session1이 모든 Client의 로그인 정보를 소유하고 있음
- 서버가 2대 이상인 경우
- 서버의 대용량 트래픽 처리를 위해 서버 2대 이상 운영이 필요할 수 있음

- Session 마다 다른 Client 로그인 정보를 가지고 있을 수 있음
- Session1 : Client1, Client2, Client3
- Session2 : Client4
- Session3 : Client5, Client6
- 만약 Client1 의 로그인 정보를 가지고 있지 않은 Server2나 Server3 에 API 요청을 하게 되면 문제가 발생하지 않을까?
- 해결 방법
1) Sticky Session : Client 마다 요청 Server를 고정
2) 세션 저장소 생성하여 모든 세션을 저장
- 해결 방법
- 세션 저장소 생성

- Session storage가 모든 Client의 로그인 정보를 소유하고 있기 때문에 모든 서버에서 모든 Client의 API 요청을 처리할 수 있음
- JWT 사용
- 로그인 정보를 Server에 저장하지 않고, Client 에 로그인 정보를 JWT로 암호화하여 저장 → JWT 통해 인증/인가

- 모든 서버에서 동일한 Secret Key 소유
- Secret Key 통한 암호화 / 위조 검증 (복호화 시)

- JWT 장/단점
- 장점
- 동시 접속자가 많을 때 서버 측 부하 낮춤
- Client, Server 가 다른 도메인을 사용할 때
- 예) 카카오 OAuth2 로그인 시 JWT Token 사용
- 단점
- 구현의 복잡도 증가
- JWT에 담는 내용이 커질 수록 네트워크 비용 증가 (클라이언트 → 서버)
- 기 생성된 JWT 를 일부만 만료시킬 방법이 없음
- Secret key 유출 시 JWT 조작 가능
- 장점
'Spring > 백엔드 기초' 카테고리의 다른 글
| [백엔드 기초] Filter, Spring Security (0) | 2025.10.24 |
|---|---|
| [백엔드 기초] 쿠키를 활용한 인증 인가 개념 실습 (0) | 2025.10.23 |
| [백엔드 기초] 기초 개념 정리하기 (0) | 2025.10.21 |
| [Spring] 스프링을 위한 기본 개념 복습 및 학습 -2 (0) | 2025.10.15 |
| [Spring] 스프링을 위한 기본 개념 복습 및 학습 (0) | 2025.10.14 |