primary-replica / ExceptionHandler / security-authenticaion / CS 스터디(항해일지 25일차)

김형준·2022년 6월 2일
0

TIL&WIL

목록 보기
25/45
post-thumbnail

1. 학습 및 개발 일지


1) 숙련 주차 과제_OAuth2.0 구글 로그인 구현

  • 🔗코드 GitHub
  • 개발 내역
    • AOP (Global Exception Handler) 적용
    • OOP (회원 가입 시 회원 정보 Validation check 모듈화)
    • OAuth2.0 구글 로그인 (회원가입 및 강제로그인) 구현

2) 현업에서의 DB 운용 방식

  • Primary / Replica 운영방식
    • 쓰기 전용 DB (Primary) 와 읽기 전용 DB (Replica) 를 구분
    • 1) Primary: 쓰기 전용
      • @Transactional 의 readOnly 속성 -> @Transactional(readOnly = false)
      • readOnly를 적지 않으면 디폴트는 false이다. 즉, primary를 바라보게 된다.
      • Write 된 Data (Create, Update, Delete) 가 Replica 로 Sync 됨 (Replication)
    • 2) Replica (Secondary): 읽기 전용
      • @Transactional(readOnly = true)
      • 위 개념은 스프링에 Primary DB endpoint, Replica DB endpoint 를 설정해야지만 가능하다.
    • 만약 Primary에 문제가 생긴다면, Replica 중 하나가 Primary가 되어 정상 운영된다.

3) 스프링 예외 처리 방법

  • 1) 서버에서 throw new Exception으로 처리한, 예상 가능한 에러를 500에러에서 400에러로 변경하기
  • 2) Response의 Body(본문)에 에러메시지를 담아서 보내기
    • errorMessage: 에러 내용
    • httpStatus: 스프링에 선언된 HttpStaus 값
  • 백엔드 예외처리: 스프링이 제공하는 ResponseEntity 클래스 사용!
  • ResponseEntity: HTTP response object 를 위한 Wrapper
    • HTTP status code / HTTP headers / HTTP body

적용 과정

  • 먼저 ResponseEntity<body, status>의 첫 번째 인수에 들어갈 body 클래스를 구현한다. (여기에선 RestApiException.Class, 필드는 에러메시지와 상태값 정보)
  • Service에는 이미 에러를 throw하는 코드를 구현했기 때문에 변경사항이 없다.
  • Controller에서 try-catch문으로 감싸주고, catch(IllegalArgumentException e) {} 부분을 구현한다.
  • catch의 구현부에는 바디에 담을 값들을 생성하고, 클라이언트에게 내려줄 StatusCode와 함께 ResponseEntity(body, Status)에 담아서 보내준다.
  • 원래 반환 타입이 ResponseEntity가 아니라면, 정상 응답 코드의 반환 타입도 변경해준다.

스프링 Controller 예외 처리 방법 by AOP

  • 처음 구현한 방식은 메서드 마다 try catch 예외처리를 구현해주고, 리턴타입 또한 ResponseEntity로 변경해줘야 했다...🤔
  • 이 과정도 AOP로 처리할 수 있지 않을까 라는 생각을 하던 찰나에, AOP로 구현한다는 반가운 말을 들었다 ㅎㅎ
  • Controller Class 내부에 @ExceptionHandler를 구현해주면 된다.
    • @ExceptionHandler() -> 괄호 안에는 처리해 줄 Exception종류.class를 작성해준다.
    • 위에서 구현한 방법과 동일하게 내부 로직을 구현하고, ResponseEntity에 값을 담아 리턴해준다.
    @ExceptionHandler({IllegalArgumentException.class, NullPointerException.class})
    public ResponseEntity handleException(IllegalArgumentException e){
        RestApiException restApiException = new RestApiException();
        restApiException.setHttpStatus(HttpStatus.BAD_REQUEST);
        restApiException.setErrorMessage(e.getMessage());
        return new ResponseEntity(
                // HTTP body
                restApiException,
                // HTTP status code
                HttpStatus.BAD_REQUEST
        );
    }
  • AOP의 장점(재사용성, 유지보수성 향상)을 제대로 알게 되었다.
    • 이렇게 Controller 내부에 @ExceptionHandler를 적용시켜주면 Controller의 모든 메서드에 적용된다.
    • 또한 리턴 타입을 바꿔야하는 것과 같이 기존 코드를 수정할 필요가 없다.

Spring Global 예외 처리 방법 by AOP

  • @ControllerAdvice
  • @RestControllerAdvice = @ControllerAdvice + @ResponseBody
    • 즉, @RestControllerAdvice를 붙이면 return 타입이 ResponseBody에 담겨 보내지는 것
    • 에러메시지를 ResponseBody에 담아 보낼 것이기 때문에 @RestControllerAdvice를 사용한다.
@RestControllerAdvice
public class RestApiExceptionHandler {

    @ExceptionHandler(value = { IllegalArgumentException.class })
    public ResponseEntity<Object> handleApiRequestException(IllegalArgumentException ex) {
        RestApiException restApiException = new RestApiException();
        restApiException.setHttpStatus(HttpStatus.BAD_REQUEST);
        restApiException.setErrorMessage(ex.getMessage());

        return new ResponseEntity(
                restApiException,
                HttpStatus.BAD_REQUEST
        );
    }
}
  • 나아가 서비스 전체에서 사용할 에러코드(ErrorCode)들을 선언할 수도 있다.
    • 예외 발생 시 서버 및 클라이언트에서 선언한 ErrorCode 사용!
public enum ErrorCode {
    // 400 Bad Request
    DUPLICATED_FOLDER_NAME(HttpStatus.BAD_REQUEST, "400_1", "중복폴더명이 이미 존재합니다."),
    BELOW_MIN_MY_PRICE(HttpStatus.BAD_REQUEST, "400_2", "최저 희망가는 최소 " + MIN_MY_PRICE + " 원 이상으로 설정해 주세요."),

    // 404 Not Found
    NOT_FOUND_PRODUCT(HttpStatus.NOT_FOUND, "404_1", "해당 관심상품 아이디가 존재하지 않습니다."),
    NOT_FOUND_FOLDER(HttpStatus.NOT_FOUND, "404_2", "해당 폴더 아이디가 존재하지 않습니다."),
    ;

    private final HttpStatus httpStatus;
    private final String errorCode;
    private final String errorMessage;

    ErrorCode(HttpStatus httpStatus, String errorCode, String errorMessage) {
        this.httpStatus = httpStatus;
        this.errorCode = errorCode;
        this.errorMessage = errorMessage;
    }
}
  • 위와 같이 Enum Class를 구현하고(생성자 까지), Service와 같이 throw exception하는 곳에가서 exception 대신 해당 이넘값을 넣어준다. (ExceptionHandler도 수정해줘야함)
  • httpStatus: HTTP 상태코드
  • errorCode: 에러코드
    • 에러 종류별로 Unique한 에러코드를 소유
    • 국제화에 사용 가능하다 (클라이언트가 사용하는 언어에 따라 에러메시지를 다르게 보여줌)
  • errorMessage: 대표 에러메시지

4) 5주차 핵심 정리

  • OOP vs AOP
    • OOP: 핵심 기능을 모듈화 한 것 (예를들어 Validator를 따로 Class로 구현)
    • AOP: 부가 기능을 모듈화 한 것 -> 즉, AOP는 OOP를 보완하는 프로그래밍 방식이다.

-> [과제에 적용]

  • 회원가입 정보 Validation Check 유효성 검증 모듈화 -> OOP: 핵심기능의 모듈화 (UserInfoValidator.class)
  • Exception Handler 예외처리 모듈화 -> AOP: 부가기능의 모듈화 (RestApiExceptionHandler.class)

5) Spring Security Authentication 추가 학습

  • 시큐리티 세션은 Authentication 하나만 가지고 있다.
  • 이때 Authentication에는 두 가지 타입을 가질 수 있는데 하나는 UserDetails 다른 하나는 OAuth2User 타입
  • 일반 로그인 시 UserDetailsImpl에서 UserDetails타입을 implements하고 User 객체를 받는 생성자를 만들어줌
  • UserDetailsImpl에서 OAuth2User 타입을 다중 implements 하여 User와 attributes를 받는 생성자 만들어줌
  • 즉, UserDetailsImpl은 implements UserDetails, OAuth2User
  • 따라서 UserDetailsImplService는 일반 로그인 / OAuth 로그인 둘 다 계정을 만들어주는 역할을 함
  • 정확히는 계정이 아니라 Authentication에 들어갈 UserDetailsImpl을 생성하여 Authentication에 return해 주는것!
  • 리턴된 UserDetailsImpl은 Authentication에 담기고 -> Session에 저장된다

6) CS 스터디

chapter 27. 작문과 비슷한 프로그래밍

특정 작업을 하는 프로그램을 작성하는 데는 항상 많은 방법이 있다.
이러한 의미에서 프로그래밍은 작문과 비슷하다.
작문에서 문체와 어휘 등이 다양하듯, 프로그래밍에서도 같은 기능을 구현하더라도 구현 방식은 굉장히 다양하기 때문이다.

포트란, 코볼, 베이직이 성공했던 이유 중 하나는 특정 응용 분야에 집중했기 때문이다. 이 언어들은 굳이 모든 프로그래밍 과제를 처리하려고 하지 않았다.


C언어

  • 1970년대에 '시스템 프로그래밍' 용도로, 즉 어셈블러, 컴파일러, 텍스트 편집기 같은 프로그래머 도구와 심지어 운영체제까지 작성할 목적으로 사용할 언어들이 만들어졌는데 그 중 가장 성공적이었던 것은 C 언어이다.
  • C는 개발 이후 미미하게 변경돼서 오늘날의 C프로그램은 30 ~ 40년 전의 코드와 거의 비슷하다.

C++

  • 1980년대에 들어서는 규모가 매우 큰 프로그램의 복잡성 관리를 도울 의도로 설계된 언어들이 개발되었는데 그 중 C++가 대표적이다. C++는 C에서 진화했고 C 프로그램은 대부분 C++프로그램에서도 유효하지만 아닌 경우도 있다.

  • 오늘날 컴퓨터에서 사용되는 주요 프로그램 대부분은 C나 C++로 작성됐다. 대부분의 맥 소프트웨어도 C, C++와 오브젝티브(C의 변종)로 작성된다.

  • 컴퓨터에는 계속해서 더 빠른 프로세서와 용량이 큰 메모리가 장착되었고, 프로그래밍을 빠르고 편하게 하는 것이 컴퓨터가 효울적으로 돌아가도록 하는 것보다 더 중요해졌다.자바와 자바스크립트 같은 언어는 의도적으로 이러한 트레이드오프에 맞춰 설계됐다.


자바 (JAVA)

  • 원래 자바의 적용 대상은 속도는 그다지 중요하지 않지만 유연성이 중요한 가전 제품과 전자 기기 같은 작은 임베디드 시스템이었다.
  • 이후 자바는 웹페이지상에서 실행할 용도로 변경되었지만 별로 인기를 얻지 못했고, 대신 웹 서버에 널리 사용되고 있다. 또 자바는 안드로이드 앱을 작성하는 주요 언어이기도 하다.
  • C++보다 단순하지만 C보다는 더 복잡하다. 또 C보다 더 안전하기는 하지만 몇몇 위험한 특성을 제거했고 메모리에서 복잡한 자료 구조를 관리하는 일처럼 에러가 발생하기 쉬운 작업을 처리할 내장 메커니즘을 갖추고 있기 때문이다. 그래서 프로그래밍 수업에서 처음 배우는 언어로도 인지도가 높다.
import java.util.*;

class AddUp{
	public static void main(String [] args){
    	Scanner keyboard = new Scanner(System.in);
        int num, sum;
        sum = 0;
        num = keyboard.nextInt();
        while (num != 0){
        	sum += num;
            num = keyboard.nextInt();
        }
        System.out.println(sum);
    }
}

자바스크립트 (JavaScript)

  • 먼저 이름의 일부가 겹친다는 점을 제외하면 자바와 아무 관계가 없다.

  • 자바스크립트는 처음부터 웹페이지의 동적인 효과를 구현하기 위해 브라우저 내부에서 사용할 목적으로 설계되었고, 오늘날 거의 모든 웹페이지는 자바스크립트코드를 어느 정도 포함하고 있다.

  • 자바스크립트는 시험 삼아 무언가 만들어 보기 쉽다. 언어 자체가 단순하다. (리액트, 노드 분들 노여워하실까봐 지웠습니다. 🤷‍♀️ )

  • 컴파일러가 모든 브라우저에 내장되어 있어 별도로 다운로드 하지 않아도 되고 계산한 결과를 바로 볼 수 있다.

var num, sum;
sum = 0;
num = prompt("Enter new value, or 0 to end");
while (num != 0){
  sum = sum + parseInt(num);
  num = prompt("Enter new value, or 0 to end");
}
alert(sum);

파이썬 (Python)

  • 파이썬은 C, C++, 자바, 자바스크립트와 구문 규칙 면에서 약간 다른데, 가장 눈에 띄는 부분은 문장을 그룹화하는 데 중괄호 대신 들여쓰기를 사용한다는 점이다.
  • 파이썬은 처음부터 가독성에 초점을 두고 설계되었다. 파이썬은 배우기 쉽고, 생각할 수 있는 거의 모든 프로그래밍 과제에 필요한 라이브러리를 풍부하게 제공해서 가장 널리 사용되는 언어 중 하나로 자리 잡았다.
sum = 0
num = input()
while num != '0';
	sum += int(num)
    num = input()
print(sum)
profile
BackEnd Developer

0개의 댓글