Spring 숙련 1 (Bean을 수동)(복수Bean 타입)(인증 과 인과)
(준비 과정)
1. 새로운 spring 프로젝트 생성 (spring web + thyleaf + lombok)
2. build.grandle 에 추가.
dependencies {
// Security
implementation 'org.springframework.boot:spring-boot-starter-security'
3.메인 페이지에 코드 추가
@SpringBootApplication(exclude = SecurityAutoConfiguration.class) // Spring Security 인증 기능 제외
public class SpringAuthApplication {
public static void main(String[] args) {
SpringApplication.run(SpringAuthApplication.class, args);
}
}
인증 기능을 제외한 이유는 현제 는 테스트를 방해 하기 때문에 나중에 풀어 줄걸임.
Bean 수동 등록?
일반적으로 @Component를 사용하여 Bean을 자동으로 등록하는 것이 좋습니다.
- 프로젝트의 규모가 커질 수록 등록할 Bean들이 많아지기 때문에 자동등록을 사용하면 편리합니다.
- 비즈니스 로직과 관련된 클래스들은 그 수가 많기 때문에 @Controller, @Service와 같은 애너테이션들을 사용해서 Bean으로 등록하고 관리하면 개발 생산성에 유리합니다.
그럼 why? Bean 수동??
기술적인 문제 & 공통적인 관심사 를 처리할 때 수동으로 등록하는 것이 좋습니다.
공통 로그처리 & 비즈니스 로직 ( 기술 지원 Bean ) 이라 부르고 수동등록 합니다.
비즈니스 로직 Bean 보다는 그 수가 적기 때문에 수동으로 등록하기 부담스럽지 않습니다.
수동등록된 Bean에서 문제가 발생했을 때 해당 위치를 파악하기 쉽다
비밀번호를 암호화 하는 객체를 수동으로 bean 으로 하겠습니다 (기술 지원).
@Configuration
public class PasswordConfig { // passwordConfig
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
(저장 될때는 앞글자 소문자로 바뀜).
BCryptPasswordEncoder(); 는 hash 함수 입니다.
아주 강력한 Hash 메커니즘을 가지고 있음.
password 를 encode (암호화)해줄거에요.
그럼 테스트를 위해 테스트 코드를 쓰사용.
요.
@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); // 암호화할 때 사용된 값과 다른 문자열과 비교했기 때문에 false
}
}
@Autowired
사용해서 PasswordEncoder 주입을 받아오고 있습니다.
.encode(String); 암호화 랄 문자를 넣는 함수.
.matches(inputPassword, encodePassword); 비교하는 함수

같은 타입의 Bean이 2개라면?
예시를 위해 같은 타입의 클래스 3개를 만듭니다
Food.java / Pizza.java / Chicken.java
public interface Food {
void eat();
}
@Component
public class Pizza implements Food {
@Override
public void eat() {
System.out.println("피자를 먹습니다.");
}
}
@Component
public class Chicken implements Food {
@Override
public void eat() {
System.out.println("치킨을 먹습니다.");
}
}
자동을 food 를 bean 으로 등록 합니다.
등록 하는것 만으로는 오류 나지는 않음.
그럼 테스트를 위해 테스트 코드를 사용.

근대 바로 error 가 뜹니다.
Could not autowire, there is more then one bean of 'Food' type.
(Chicken , Pizza).
방법 1
피자 주입 할지 치킨 주입할지 정해 줘야 합니다.
e.g :

(Pizza 가 아니라 pizza 인 이유는 bean 등록 될때 첫글자 는 소문자로 고정됨).

여기서 알수 있는거는
@Autowired 는 기본적으로 는 bean 객체의 타입 으로 찾는다
근대 타입의 객체가 여러개면 이름 으로 찾는다.
@Test
@DisplayName("테스트1")
void test1(){
pizza.eat();
chicken.eat();
}

방법2:
@Primary 정하기
치킨을 @Primary 로 하면.
@Component
@Primary
public class Chicken implements Food {

에러가 사라지는 것을 볼수 있습니다.
food. eat 를 호출 하면 "치킨을 먹습니다" 가 나오지요.
방법3:
@Qualifier("{명칭}")
Pizza.java 에 @Qualifier(" pizza ")
@Component
@Qualifier("pizza")
public class Pizza implements Food {
테스트 Food food 위에도

결과:

치킨에서 피자로 바뀌었네요.
@Qualifier 로 객체를 지정 + 연결 해주고 있다.
@Qualifier > @Primary
하지만 @Qualifier 는 주입 받고자 하는 클래스에 선언 해 줘야함.
범용적으로 사용 되는 bean 은 @Primary.
지역적으로 사용 되는 bean 은 @Qualifier
인증과 인가란 무엇일까?
인증(Authentication) : 실제 유저인지 인증. (지문인식, 로그인 ).
인가(Authorization) : 리소스 접근 권한이 있는 ( 페이지-관리자 권한 ).

일반적으로 서버-클라이언트 구조로 되어있고, 실제로 이 두가지 요소는 아주 멀리 떨어져 있습니다.
비연결성 : 실제로 클라상 서버는 연결 되어 있지 않다.(리소스 절약)
하나의 응답 만 하고 연결을 끊어 버림.
무상태(Stateless) : 클라이언트의 상태를 저장하지 않는다
기존의 상태를 저장하는 것들도 서버의 비용과 부담을 증가시키는 것
기존의 상태가 없다고 가정하는 프로토콜을 이용해 구현.
서버는 클라이언트가 직전에, 혹은 그 전에 어떠한 요청을 보냈는지 관심도 없고 전혀 알지 못합니다.
//
q : 이전의 정보들이 잘 있는것처럼 연속성있게 사용해왔는데요?
ans : 실제로 그렇게 느껴지기 위해서 개발자들이 매우 많은 노력을 기울이고 있습니다!
네이버 뉴스탭에 스포츠탭에 특정 기사를 본다고 생각하면 /News/Sports/9
url을 계층적으로 설계 , , 다음 요청에 대한 api url을 이전 계층에만 둔다.
q : 그렇다면 인증과 같이, 해당 유저가 인증을 통과했다는 사실은 상태값이 아닌가요?
ans : “유저가 인증되었다”라는 정보를 유지시켜야 한다는
과제를 어떻게 해결했는지 관점에서 보시면 좋을 것 같습니다.
인증의 방식
1. 쿠키-세션 (Session / Cookie).

2. JWT 기반 인증 (Json Web Token).
JWT 토큰(Access Token)을 HTTP 헤더에 실어 서버가 클라이언트를 식별합니다.
