Spring

Spring 숙련 1 (Bean을 수동)(복수Bean 타입)(인증 과 인과)

sehunbang 2024. 1. 24. 15:28

(준비 과정)

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 헤더에 실어 서버가 클라이언트를 식별합니다.