📚 목차
🧩 Bean을 수동으로 등록하는 방법
프로젝트 생성하기


Bean 수동 등록
- 일반적으로는 @Component를 사용하여 Bean을 자동으로 등록하는 것이 좋다.
- 기술적인 문제나 공통적인 관심사를 처리할 때 사용하는 객체들을 수동으로 등록하는 것이 좋다.
- 공통 로그처리와 같은 비즈니스 로직을 지원하기 위한 부가 적이고 공통적인 기능들을 기술 지원 Bean이라 부르고 수동등록한다.
- 수동등록된 Bean에서 문제가 발생했을 때 해당 위치를 파악하기 쉽다는 장점이 있다.
- /config/PasswordConfig.java : @Bean이 설정된 메서드 passwordEncoder로 Bean 이름이 설정된다.
@Configuration
public class PasswordConfig {
@Bean // 수동 등록
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
- /test/.../PasswordEncoderTest.java
package com.sparta.springauth;
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;
import org.springframework.security.crypto.password.PasswordEncoder;
@SpringBootTest
public class PasswordEncoderTest {
@Autowired
PasswordEncoder passwordEncoder;
@Test
@DisplayName("수동 등록한 passwordEncoder를 주입 받아와 문자열 암호화")
void test1() {
String password = "Robbie's password";
String encodePassword = passwordEncoder.encode(password);
System.out.println("encodePassword = " + encodePassword);
String inputPassword = "Robbie";
boolean matches = passwordEncoder.matches(inputPassword, encodePassword);
System.out.println("matches = " + matches);
}
}

🧩 같은 타입의 Bean이 2개라면?
같은 타입 Bean 등록
- food 인터페이스
package com.sparta.springauth.food;
public interface Food {
void eat();
}
- Chicken (Bean)
package com.sparta.springauth.food;
import org.springframework.stereotype.Component;
@Component
public class Chicken implements Food {
@Override
public void eat() {
System.out.println("치킨을 먹습니다.");
}
}
- Pizza (Bean)
package com.sparta.springauth.food;
import org.springframework.stereotype.Component;
@Component
public class Pizza implements Food {
@Override
public void eat() {
System.out.println("피자를 먹습니다.");
}
}

- /test/BeanTest.java
@SpringBootTest
public class BeanTest {
@Autowired
Food food;
}

- 등록된 Bean 이름 명시하기
@SpringBootTest
public class BeanTest {
@Autowired
Food pizza;
@Autowired
Food chicken;
@Test
void test1() {
pizza.eat();
chicken.eat();
}
}
@Autowired가 기본적으로는 Bean Type(Food)으로 DI를 지원하며, 연결이 되지않을 경우 Bean Name(pizza, chicken)으로 찾는 다는 것을 알 수 있다.

- @Primary 사용하기: 같은 타입의 Bean이 여러 개 있더라도 우선 @Primary가 설정된 Bean 객체를 주입한다.
@Component
@Primary // ← 추가
public class Chicken implements Food {
@Override
public void eat() {
System.out.println("치킨을 먹습니다.");
}
}


- @Qualifier 사용하기
- 우선순위: @Qualifier > @Primary
- 같은 타입의 Bean이 여러 개 있을 때는 범용적으로 사용되는 Bean 객체에는 @Primary를 설정하고 지엽적으로 사용되는 Bean 객체에는 @Qualifier를 사용하는 것이 좋다.
일반적으로 Spring에서는 중복되는 설정 범위가 있을 때 좁은 범위의 설정이 더 우선순위가 높다.
@SpringBootTest
public class BeanTest {
@Autowired
@Qualifier("pizza") // ← 추가
Food food;
@Test
void test1() {
food.eat();
}
}

🧩 인증과 인가란?
인증 vs 인가
- 인증(Authentication): 해당 유저가 실제 유저인지 인증하는 개념 (로그인)
- 인가(Authorization): 해당 유저가 특정 리소스에 접근이 가능한지 허가를 확인하는 개념 (회원-비회원, 관리자 권환)
웹 애플리케이션 인증의 특징
- 비연결성(Connectionless): 서버와 클라이언트가 연결되어 있지 않음
- 무상태(Stateless): 서버가 클라이언트의 상태를 저장하지 않음
인증의 방식
- 쿠키-세션 방식
- 특정 유저가 로그인 되었다는 상태를 저장하는 방식
- 인증과 관련된 아주 약간의 정보만 서버가 가지고 있게 되고 유저의 이전 상태의 전부는 아니더라도 인증과 관련된 최소한의 정보는 저장해서 로그인을 유지시킨다는 개념
- JWT(JSON Web Token)
- 인증에 필요한 정보들을 암호화시킨 토큰
- JWT 기반 인증은 쿠키-세션 방식과 유사하게 JWT 토큰(Access Token)을 HTTP 헤더에 실어 서버가 클라이언트를 식별함
🧩 쿠키와 세션이란?
쿠키와 세션 모두 HTTP 에 상태 정보를 유지(Stateful)하기 위해 사용된다. 즉, 쿠키와 세션을 통해 서버에서는 클라이언트 별로 인증 및 인가를 할 수 있게 된다.
쿠키와 세션
- 쿠키
- 클라이언트에 저장될 목적으로 생성한 작은 정보를 담은 파일
- (크롬 기준) Application-Storage-Cookies에 도메인 별로 저장됨
- 구성 요소: Name, Value, Domain, Path, Expires
- 세션
- 서버에서 일정시간 동안 클라이언트 상태를 유지하기 위해 사용
- 서버에서 클라이언트 별로 유일무이한 세션 ID를 부여한 후 클라이언트 별 필요한 정보를 서버에 저장함
- 서버에서 생성한 세션 ID는 클라이언트의 쿠키값(세션 쿠키)으로 저장되어 클라이언트 식별에 사용됨
| 구분 | 쿠키 | 세션 |
| 설명 | 클라이언트에 저장될 목적으로 생성한 작은 정보를 담은 파일 | 서버에서 일정시간 동안 클라이언트 상태를 유지하기 위해 사용 |
| 저장 위치 | 클라이언트 (웹 브라우저) | 웹 서버 |
| 사용 예시 | 사이트 팝업의 오늘 다시보지 않기 정보 저장 | 로그인 정보 저장 |
| 만료 시점 | 쿠키 저장 시 만료일시 설정 가능 (브라우저 종료시도 유지 가능) |
▪ 브라우저 종료 시까지 ▪ 클라이언트 로그아웃 시까지 ▪ 서버에 설정한 유지기간까지 해당 클라이언트의 재요청이 없는 경우 |
| 용량 제한 | ▪ 하나의 도메인 당 180개 ▪ 하나의 쿠키 당 4KB |
개수 제한 없음 |
| 보안 | 취약 | 비교적 안전 |
쿠키 다루기
- AuthController 생성
@RestController
@RequestMapping("/api")
public class AuthController {
public static final String AUTHORIZATION_HEADER = "Authorization";
@GetMapping("/create-cookie")
public String createCookie(HttpServletResponse res) {
addCookie("Robbie Auth", res);
return "createCookie";
}
@GetMapping("/get-cookie")
public String getCookie(@CookieValue(AUTHORIZATION_HEADER) String value) {
System.out.println("value = " + value);
return "getCookie : " + value;
}
public static void addCookie(String cookieValue, HttpServletResponse res) {
try {
cookieValue = URLEncoder.encode(cookieValue, "utf-8").replaceAll("\\+", "%20");
Cookie cookie = new Cookie(AUTHORIZATION_HEADER, cookieValue);
cookie.setPath("/");
cookie.setMaxAge(30 * 60);
res.addCookie(cookie);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e.getMessage());
}
}
}
- createCookie 테스트: 웹 브라우저에 http://localhost:8080/api/create-cookie를 입력한 후 접속을 하면…

- SpringAuthApplication 수정: 기본 로그인 페이지를 없애기 위해서 Spring Security의 기능을 제한 해야한다고 한다.
@SpringBootApplication(exclude = SecurityAutoConfiguration.class) // ← 추가
public class SpringAuthApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAuthApplication.class, args);
}
}

- SecurityConfig 생성
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.anyRequest().permitAll()
);
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
return new InMemoryUserDetailsManager();
}
}
- SpringAuthApplication 원복
@SpringBootApplication
public class SpringAuthApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAuthApplication.class, args);
}
}

- getCookie 테스트: 웹 브라우저에 http://localhost:8080/api/get-cookie를 입력한 후 접속을 하면…

- AuthController 수정: createSession(), getSession() 구현
@GetMapping("/create-session")
public String createSession(HttpServletRequest req) {
HttpSession session = req.getSession(true);
session.setAttribute(AUTHORIZATION_HEADER, "Robbie Auth");
return "createSession";
}
@GetMapping("/get-session")
public String getSession(HttpServletRequest req) {
HttpSession session = req.getSession(false);
String value = (String) session.getAttribute(AUTHORIZATION_HEADER);
System.out.println("value = " + value);
return "getSession : " + value;
}


'내일배움캠프' 카테고리의 다른 글
| [내일배움캠프] 필터, Spring Security, Validation (0) | 2026.04.08 |
|---|---|
| [내일배움캠프] JWT을 사용한 회원가입과 로그인 구현 (0) | 2026.04.08 |
| [내일배움캠프] JPA와 Entity, 영속성 컨텍스트 (0) | 2026.04.06 |
| [내일배움캠프] IoC, DI, Bean (0) | 2026.04.06 |
| [내일배움캠프] Spring 입문 1주차 (0) | 2026.04.06 |