[TIL] DI, IoC ,인증 , 인가 , 스케줄링

hyewon jeong·2022년 12월 15일
0

TIL

목록 보기
1/138
post-thumbnail

스프링 프레임워크가 필요한 객체를 생성하여 관리하는 역할을 대신해 줍니다.

  • 스프링 '빈' 사용 방법
    1. @Autowired

      • 멤버변수 선언 위에 @Autowired → 스프링에 의해 DI (의존성 주입) 됨
      • 주입하려는 변수 위에 다가 @ Autowired 선언 하면 생성된 빈 객체를 자동으로 스프링이 넣어줌
        @Component
        public class ProductService {
        		
            **@Autowired**
            private ProductRepository productRepository;
        		
        		// ...
        }
      • '빈' 을 사용할 함수 위에 @Autowired → 스프링에 의해 DI 됨
      • 변수를 final로 만든 다음에 생성자를 만들고 그 위에 @ Autowired 추가
@Component
public class ProductService {

    private final ProductRepository productRepository;

    @Autowired
    public ProductService(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }
		
		// ...
}
  • @Autowired 적용 조건

    • 스프링 IoC 컨테이너에 의해 관리되는 클래스에서만 가능 Untitled
  • @Autowired 생략 조건

    • Spring 4.3 버젼 부터 @Autowired 생략가능

    • 생성자 선언이 1개 일 때만 생략 가능

      • 파라미터가 다른 생성자들

        public class A {
        	@Autowired // 생략 불가
        	public A(B b) { ... }
        
        	@Autowired // 생략 불가
        	public A(B b, C c) { ... }
        }
    • Lombok 의 @RequiredArgsConstructor 를 사용하면 다음과 같이 코딩 가능

      @RestController
      **@RequiredArgsConstructor** // final로 선언된 멤버 변수를 자동으로 생성합니다.
      public class ProductController {
      
          private final ProductService productService;
          
          // 생략 가능
      		// @Autowired
      		// public ProductController(ProductService productService) {
      		//     this.productService = productService;
      		// }
      }
      • ApplicationContext
        • 스프링 IoC 컨테이너에서 빈을 수동으로 가져오는 방법
          @Component
          public class ProductService {
          
              private final ProductRepository productRepository;
          
              @Autowired
              public ProductService(ApplicationContext context) {
                  // 1.'빈' 이름으로 가져오기
                  ProductRepository productRepository = (ProductRepository) context.getBean("productRepository");
                  // 2.'빈' 클래스 형식으로 가져오기
                  // ProductRepository productRepository = context.getBean(ProductRepository.class);
                  this.productRepository = productRepository;
              }
          
          		// ...		
          }
      
                         // 초, 분, 시, 일, 월, 주 순서
                         @Scheduled(cron = "0 0 1 * * *")
                         public void updatePrice() throws InterruptedException {
                             log.info("가격 업데이트 실행");
                             List<Product> productList = productRepository.findAll();
                             for (Product product : productList) {
                                 // 1초에 한 상품 씩 조회합니다 (NAVER 제한)
                                 TimeUnit.SECONDS.sleep(1);
                     
                                 String title = product.getTitle();
                                 List<ItemDto> itemDtoList = naverApiService.searchItems(title);
                                 ItemDto itemDto = itemDtoList.get(0);
                     
                                 // i 번째 관심 상품 정보를 업데이트합니다.
                                 Long id = product.getId();
                                 productService.updateBySearch(id, itemDto);
                             }
                         }
                     }
                     ```
                     

스프링 스케줄 설정 법

            ```java
            @SpringBootApplication
            @EnableScheduling
            public class Scheduler {     @Scheduled(cron = "10 * * * * *")    
                public void run() {       
                          // TODO        
                          System.out.println("현재 시간은 " + new Date()); 
               }   
             }
            ```
            

클래스 위에 @EnableScheduling 어노테이션을 설정 해 줍니다.

그리고 실제 사용할 자바 매소드 위에

예시)

@Scheduled(cron = "10 * * * * *")

이런식으로 설정해 줍니다.

결과: 이렇게 아래처럼 매분 10초마다 스케줄러 도는것을 볼 수 있습니다.

CRON 표현식

            스케줄러의 경우 매소드위에 **@Scheduled(cron = "10 * * * * *")** 이런식으로 어노테이션을 작성해 줍니다.
            
            그럼 CRON 표현식을 살펴 보겠습니다.

            

*: 모든조건(ALL)을 의미 합니다.

? : 설정 값 없을 때(어떠한 값이든 상관없습니다.)
다만 날짜와 요일에서만 사용가능 합니다.

  • (하이픈) : 범위값을 지정할 때 사용 합니다.

,(콤마) : 여러값을 지정할 때 사용 합니다.

/(슬러시) : 초기값과 증가치를 설정할 때 사용 합니다.

L : 마지막 - 지정할 수 있는 범위의 마지막 값 설정할때 사용 가능 합니다.
그리고 날짜와 요일에서만 사용 가능 합니다.

W : 가장 가까운 평일 찾는다 - 일 에서만 사용가능

예) 10W

  • 10일이 평일 일 때 : 10일에 실행
  • 10일이 토요일 일 때 : 가장 가까운 평일인 금요일(9일)에 참
  • 10일이 일요일 일 때 : 가장 가까운 평일인 월요일(11일)에 참

인증과 인가 개념 이해

바로 스프링 시큐리티…?

🙃 스프링에서 대표적으로 아래에서 배울 “인증”과 “인가”를 관리해주는 것은 바로 스프링 시큐리티 입니다. 다만 우리는 기존에 다른 사람의 코드를 사용해서 구현하는등 실질적으로 인증과 인가에 대해서 고민하거나 구현해 본 적이 없습니다.

해당 개념에 대한 이해가 없는 상태에서, 바로 스프링 시큐리티를 적용하는것은 너무 어렵기만하고, 재미도 없고, 매번 남의 코드 따라치는 느낌으로 코딩하게 될 수 있어서 이번 주차에는 간단하게 “인증”하는 로직을 직접 구현해보고, 심화주차에 스프링 시큐리티에 대하여 다룰 예정입니다.

인증 개념 자체는 어려운 로직이 아닌 만큼 천천히 코드를 따라해 보시고,
지금 프로젝트의 불안함과 불편함같은 개선점을 다음 주차에 어떻게 개선할 수 있을지에 대한 관점으로 가볍게 들어주시면 좋을 것 같습니다!

01. 복습 - 인증과 인가

📌 인증(Authentication)은 해당 유저가 실제 유저인지 인증하는 개념입니다. 여러분의 스마트폰에 지문인식, 이용하는 사이트에 로그인 등과 같이, 실제 그 유저가 맞는지를 확인하는 절차 입니다.

인가(Authorization)는 해당 유저가 특정 리소스에 접근이 가능한지 허가를 확인하는 개념입니다. 예를들어 관리자 페이지-관리자 권한 같은 것들을 들 수 있겠네요

우리가 인증과 인가를 헷갈려 하는 이유는 로그인만 생각해서 입니다.
우리가 자주 하는 로그인은 인증을 할 때(비밀번호 입력하고 제출 할 때)
회원/비회원 여부에 따라 갈리는 인가를 받기 때문입니다.
앞으로는 그 두가지 개념을 분리해서 생각해보시면 조금 더 이해가 쉬워질 것 같네요

02. 인증의 방식

🤔 인증과 인가를 분리해서 생각해 보시라고 말씀드렸죠?
지금 다룰 내용도 마찬가지 입니다.
우리는 이 챕터가 “인증”하는 여러가지 방식 중 두가지를 배울 것 입니다.
인가나 보안 전반에 대한 이야기의 전부가 아니라는 점을 명심하시면 좋을 것 같습니다.

📌 인증의 방식을 이해하는데 있어, 가장 중요한 내용은 한가지 입니다.
“우리는 웹 어플리케이션의 인증을 배운다.”

  • “웹 어플리케이션의 인증”은 어떠한 특수성이 있을까요?
    1. 일반적으로 서버-클라이언트 구조로 되어있고, 실제로 이 두가지 요소는 아주 멀리 떨어져 있습니다.
    2. 그리고 Http 라는 프로토콜을 이용하여 통신하는데, 그 통신은 비연결성(Connectionless) 무상태(Stateless)로 이루어집니다.

🤔 비연결성, 무상태가 뭔가요?

👉 비연결성(Connectionless)은 서버와 클라이언트가 연결되어 있지 않다는 것 입니다.
채팅이나 게임 같은 것들을 하지 않는 이상 서버와 클라이언트는 실제로 연결되어 있지 않습니다.
그 이유는 리소스를 절약하기 위해서 인데, 만약 서버와 클라이언트가 실제로 계속 연결되어있다면 클라이언트는 그렇다고 쳐도, 서버의 비용이 기하급수적으로 늘어나기 때문이죠.
그래서 서버는 실제로 하나의 요청에 하나의 응답을 내버리고 연결을 끊어버리고있다 라고 생각하시면 좋습니다.

무상태(Stateless)는 서버가 클라이언트의 상태를 저장하지 않는다는 것입니다.
기존의 상태를 저장하는 것들도 마찬가지로 서버의 비용과 부담을 증가시키는 것 이기 때문에 기존의 상태가 없다고 가정하는 프로토콜을 이용해 구현되어 있습니다.
실제로 서버는 클라이언트가 직전에, 혹은 그 전에 어떠한 요청을 보냈는지 관심도 없고 전혀 알지 못합니다.

🤔 아니, 근데 우리가 인터넷 사용할때는 이전의 정보들이 잘 있는것처럼 연속성있게 사용해왔는데요?

👉 실제로 그렇게 느껴지기 위해서 개발자들이 매우 많은 노력을 기울이고 있습니다!

예를들어 여러분이 네이버 뉴스탭에 스포츠탭에 특정 기사를 본다고 생각하면
”/News/Sports/9”
와 같이 url을 계층적으로 설계하고, 다음 요청에 대한 api url을 이전 계층에만 둔다면 연속적으로 사용하고 있다고 느끼실 수 있겠죠?

🤔 그렇다면 인증과 같이, 해당 유저가 인증을 통과했다는 사실은 상태값이 아닌가요?

👉 그 부분을 지금부터 설명하려고 합니다!
모든 것들을 이해하실 필요는 없고 어떻게 비연결성, 무상태 프로토콜에서 “유저가 인증되었다”라는 정보를 유지시켜야 한다는 과제를 어떻게 해결했는지 관점에서 보시면 좋을 것 같습니다.

📌 일반적으로 웹 어플리케이션은 아래 두가지 방법을 통해서 인증을 처리합니다

쿠키-세션 방식의 인증

📌 쿠키-세션 방식은 서버가 특정 유저가 로그인 되었다는 상태를 저장하는 방식입니다.
인증과 관련된 아주 약간의 정보만 서버가 가지고 있게 되고 유저의 이전 상태의 전부는 아니더라도
인증과 관련된 최소한의 정보는 저장해서 로그인을 유지시킨다는 개념이죠

  • 아래는 그림의 각 번호에 따른 설명입니다
    1. 사용자가 로그인 요청을 보냅니다.
    2. 서버는 DB의 유저 테이블을 뒤져서 아이디 비밀번호를 대조해봐야겠죠?
    3. 실제 유저테이블의 정보와 일치한다면 인증을 통과한 것으로 보고 “세션 저장소”에 해당 유저가 로그인 되었다는 정보를 넣습니다.
    4. 세션 저장소에서는 유저의 정보와는 관련 없는 난수인 session-id를 발급합니다.
    5. 서버는 로그인 요청의 응답으로 session-id를 내어줍니다.
    6. 클라이언트는 그 session-id를 쿠키라는 저장소에 보관하고 앞으로의 요청마다 세션아이디를 같이 보냅니다. (주로 HTTP header에 담아서 보냅니다!)
    7. 클라이언트의 요청에서 쿠키를 발견했다면 서버는 세션 저장소에서 쿠키를 검증합니다.
    8. 만약 유저정보를 받아왔다면 이 사용자는 로그인이 되어있는 사용자겠죠?
    9. 이후에는 로그인 된 유저에 따른 응답을 내어줍니다.

    JWT 기반 인증

           

📌 JWT(JSON Web Token)란 인증에 필요한 정보들을 암호화시킨 토큰을 의미합니다. JWT 기반 인증은 쿠키/세션 방식과 유사하게 JWT 토큰(Access Token)을 HTTP 헤더에 실어 서버가 클라이언트를 식별합니다.

🤔 JWT가 뭔가요?

아래는 그림의 각 번호에 따른 설명입니다
1. 사용자가 로그인 요청을 보냅니다.
2. 서버는 DB의 유저 테이블을 뒤져서 아이디 비밀번호를 대조해봐야겠죠?
3. 실제 유저테이블의 정보와 일치한다면 인증을 통과한 것으로 보고 유저의 정보를 JWT로 암호화 해서 내보냅니다
4. 서버는 로그인 요청의 응답으로 jwt 토큰을 내어줍니다.
5. 클라이언트는 그 토큰을 저장소에 보관하고 앞으로의 요청마다 토큰을 같이 보냅니다.
6. 클라이언트의 요청에서 토큰을 발견했다면 서버는 토큰을 검증합니다.
7. 이후에는 로그인 된 유저에 따른 응답을 내어줍니다.

👉 말 그대로 인증에 필요한 정보들을 암호화시킨 토큰을 의미합니다

실제로 이렇게 생겼고, 자세히 보시면 ’ . ’을 사용하여 세 부분으로 나뉘어져 있습니다.

위는 암호화 알고리즘에 의해 암호화 된 모양이고,
해독하면 아래와 같은 모습입니다.

가운데에, 실제 유저의 정보가 들어있고, HEADER와 VERIFY SIGNATURE부분은 암호화 관련된 정보 양식이라고 생각하시면 좋겠네요.

profile
개발자꿈나무

0개의 댓글