[새배내] 스프링 - 협업 미션을 하면서 새로 배운 내용

Junseo Kim·2021년 5월 20일
0

[우아한테크코스3기]

목록 보기
20/27
post-thumbnail

2021.05.20 ~ 2021.06.04


Docker

컨테이너 기반의 오픈소스 가상화 플랫폼. 서버 내에 많은 컨테이너를 두고, 각 컨테이너로 다양한 프로그램이나 실행환경을 추상화 한 것이다. 동일한 인터페이스를 제공하여 프로그램의 배포 및 관리를 단순화 시켜준 것이다.

참고
[DevOps] Docker란 무엇인가?

Dockerfile vs docker.compose.yml

Dockerfile : 이미지를 실행하면서 특정 작업까지 같이 처리하게 해주는 도구. 사용자가 이미지를 조립하기 위해 호출 할 수 있는 명령이 포함된 간단한 텍스트 파일

Docker-compose : 다수의 컨테이너를 쉽게 실행할 수 있게 도와주는 도구.

-> docker-compose로 컨테이너를 자동 생성한 후, Dockerfile 생성한 컨테이너 안에 자동을 세팅 작업까지 돌아가게 할 수 있다. docker-compose.yml에서 모든 것을 정의할 수도 있지만, Dockerfile과 docker-compose.yml을 모두 사용하면 더 효율적이다.

  1. 이미지 파일들을 빌드하기 위한 Dockerfile 생성
  2. docker-compose.yml 내에서 해당 Dockerfile 이미지를 기반으로 복잡한 스택 처리
  3. docker compose 명령어로 전체 스택 배포
version: '3'
services:
  web:
    build: . # 현재 디렉토리에서 빌드 명령(Dockerfile)을 실행함
    ports:
     - "8080:80"
  db:
    image: mysql
    ports:
      - "13306:3306"
    environment:
      - MYSQL_ROOT_PASSWORD=root
      - MYSQL_USER=air
      - MYSQL_PASSWORD=air
      - MYSQL_DATABASE=subway

(더 알아볼 것!)

참고
DockerFile과 Docker-Compose
What is the difference between Dockerfile and docker-compose.yml files?

@Min & @Max vs @Length or @Size

Java validation 어노테이션 중

@Min & @Max는 숫자에 사용한다. String에 사용하면 에러 발생!

@Length나 @Size는 String의 길이를 검증할 때 사용한다. 두 어노테이션의 기능상 차이는 없다.

@NullAndEmptySource

junit @ParameterizedTest시 메서드 단에 @NullAndEmptySource를 붙여주면 Null과 Empty값도 같이 테스트 해 볼 수 있다.

인텔리제이 빌드 vs gradle 빌드

인텔리제이는 @RequestBody로 값을 바인딩할 때 내부적으로 jackson을 사용해서 기본 생성자가 필요하다.

gradle은 gson을 사용하며 기본 생성자가 굳이 없어도 값을 바인딜 할 수 있다.

해싱

보통 비밀번호는 해싱해서 db에 저장한다.

해싱 알고리즘을 통해 비밀번호를 해싱하는데, 해싱은 단방향이므로 원래대로 돌릴 수는 없다. 그래서 비밀번호를 비교할때 들어온 비밀번호값도 해싱 후 비교한다.

이때 보안을 위해서 salt를 사용한다. salt란 비밀번호에 추가로 다른 값을 넣는 걸 말한다. 해싱할때 값이 조금만 달라져도 해싱값이 완전 바뀌기 때문에 해커들이 찾기 어려워진다. 이때 salt 값은 db에 같이 저장해둔다. 로그인 할 때 해당 salt값을 통해 해싱해야하기 때문이다.

또 keyChain이라는 작업도 해준다. 서버에서 횟수를 정해놓고, 해싱한 결과값을 해당 횟수만큼 해싱을 또 하는 것이다. 이렇게하면 원래 비밀번호를 알기 더욱 어려워진다.

AOP

aop란 관점 지향 프로그래밍을 뜻한다. 간단하게 AOP 비즈니스 로직과 분리되는 횡단 관심사를 분리하는 방식이다. 흔히 로그를 찍을 때 많이 사용한다. 로그가 필요한 메서드 마다 똑같은 로그찍는 코드를 하나하나 추가해주는 것은 매우 비효율적이다. 이럴때 aop를 사용한다.

예외를 잡을 때 사용하는 @ControllerAdvice 또한 aop를 이용하는 방식이다.

다른 저장소 cherry-pick 하기

git remote add 별명 다른_저장소_주소 // 다른 저장소 remote 등록하기

git fetch 별명 원하는_브랜치명 // 가져오기 원하는 브랜치 fetch

git cherry-pick 시작_커밋^..마지막_커밋

위의 순서로 진행한 후, conflict나는 부분을 하나씩 고쳐가면 된다.

커밋별로 confilct 고친 후, git add, git commit, git cherry-pick --conitnue 순으로 진행

// 예시

git add * // conflict 고친 부분 add

git commit -m "feat : station 예외 처리 로직 구현" // commit 하기 

git cherry-pick --continue // 다음 commit cherry-pick 시도

루트킷

시스템에 전반적으로 접근할 수 있는 루트 권한을 쉽게 얻게 해주는 킷이다. 루트킷을 사용하여 파일이나 레지스트리를 숨길 수 있다. 악성 프로그램이나 바이러스를 시스템에서 제거하려는 시도를 무력화 할 수 있다.

참고
루트킷-1: 네 정체를 밝혀라

CORS

Cross-Origin Resource Sharing의 약자로 브라우저에서 실행 중인 스크립트에서 시작되는 cross-origin HTTP 요청을 제한하는 브라우저 보안 기능을 말한다.

아래의 4가지 경우에 요청을 차단한다.

  • 다른 도메인
  • 다른 하위 도메인
  • 다른 포트
  • 다른 프로토콜

spring에서 CORS 설정을 해주면 해결할 수 있다.

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedMethods("GET", "POST", "DELETE", "PUT", "OPTIONS", "PATCH")
                .allowedOrigins("*") // 생략 가능한 것 같다.
                .exposedHeaders(HttpHeaders.LOCATION);
    }
}

preflight

요청을 미리 보내보는 것이다.

기본적으로 브라우저는 cross-origin 요청을 전송하기 전에 OPTIONS 요청으로 preflight를 먼저 전송한다.

OPTIONS 요청의 응답으로 Access-Control-Allow-Origin(어떤 origin을 허용하는지), Access-Control-Allow-Methods(어떤 method를 허용하는지)가 넘어온다.

브라우저가 결과를 확인하고 난 후, cross-origin 요청을 보낸다.

그렇다고 매 요청시 preflight를 보내는 것은 아니다. 서버 설정을 통해 preflight 결과를 캐싱해둔다. 이 정보가 있는 동안은 preflight를 생략하고 요청을 바로 보낼 수 있다.

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedMethods("GET", "POST", "DELETE", "PUT", "OPTIONS", "PATCH")
                .allowedOrigins("*")
                .maxAge(3600) // 3600초 동안 preflight 캐시 정보 저장 
                .exposedHeaders(HttpHeaders.LOCATION);
    }
}

참고
[Spring Boot] CORS와 Preflight에 관한 이슈

http 상태코드 결정

301 & 302 vs 307 & 308

301, 302는 리다이렉트 시 http method를 GET요청으로 변환한다.
307, 308은 리다이렉트 시 http method를 그대로 유지해준다.

따라서 get요청 시는 상관없지만 post 요청 시는 리다이렉트 되면서 메서드가 get으로 바뀌어버려 오류가 날 수 있다.

예를 들어 http요청을 https로 리다이렉트 시킬때, 301, 302를 사용하면, POST http://~ 요청이 GET https://~로 리다이렉트 되지만, 307, 308을 사용하면 POST http://~ 요청이 POST https://~로 리다이렉트 된다.

301은 308과 유사하고, 302는 307과 유사하다. 301, 308은 영구적인 리다이렉트를 뜻하고, 302, 307은 임시 리다이렉트를 뜻한다.

출처
(HTTP) 상태 코드 - 307 vs 308
Choosing an HTTP Status Code

ApplicationContext

스프링 컨테이너이다. 스프링 애플리케이션 전반에 걸쳐 모든 구성요소의 제어 작업을 담당하는 IoC 엔진. 빈을 생성하고 관계를 설정하는 일종의 빈 팩토리라고 볼 수 있다.(빈 팩토리를 좀 더 확장한 개념으로 ApplicationContext 인터페이스는 BeanFactory 인터페이스를 상속한 서브인터페이스이다.)

실제로 스프링 컨테이너 또는 IoC 컨테이너라고 말하는 것은 바로 이 ApplicationContext 인터페이스를 구현한 클래스의 객체이다.

애플리케이션 컨텍스트는 별도의 정보를 참고하여 빈의 생성, 관계 설정 등의 제어를 총괄한다.

또한 싱글톤을 저장하고 관리하는 싱글톤 저장소이다. 스프링은 기본적으로 내부에서 생성하는 빈 객체를 모두 싱글톤으로 만든다.

참고
스프링(Spring) 프레임워크 기본 개념 강좌 (3) - IoC
[Spring] Ioc, DI, ApplicationContext, Bean, BeanFactory

@Value

프로퍼티 파일의 값을 가져올 때 사용. 이때 프로퍼티 값을 사용하려는 객체는 Bean으로 등록해야한다. 실제로 스프링 컨테이너 내부에서는 빈을 등록하는시점에 @Value 어노테이션이 붙어있다면 application.yml에서 찾아 값을 넣어준다.

# applicatoin.yml
security:
  jwt:
    token:
      secret-key: my_secret_is_secret
      expire-length: 3600000
@Component // 빈으로 등록!
public class JwtTokenProvider {
    // application.yml의 프로퍼티 값을 가져온다.
    @Value("${security.jwt.token.secret-key}")
    private String secretKey;
    @Value("${security.jwt.token.expire-length}")
    private long validityInMilliseconds;
    
    // ...
}

on delete cascade

외래키로 연결된 튜플을 같이 삭제해주는 sql 문법이다.

create table if not exists SECTION
(
    id bigint auto_increment not null,
    line_id bigint not null,
    up_station_id bigint not null,
    down_station_id bigint not null,
    distance int not null,
    primary key(id),
    foreign key (up_station_id) references station(id),
    foreign key (down_station_id) references station(id),
    foreign key (line_id) references line(id) on delete cascade // line 이 지워지면 section도 지워짐.
);

위의 예시로는 Section이 foreign key로 Line을 가리키고 있을 때, Line을 삭제하면 Section 도 같이 삭제된다.

VPC과 서브넷 일단은 간단 개념만!

VPC

Virtual Private Cloud의 약자이다. AWS 계정 전용 가상 네트워크를 말한다. 논리적으로 격리되어있다.

서브넷

VPC를 쪼개는 과정. 더 많은 네트워크 망을 만들기 위해서 나눈다.

참고
[AWS] 가장쉽게 VPC 개념잡기

AWS I am role

AWS 리소스에 대한 액세스를 안전하게 제어할 수 있는 웹 서비스이다. I AM을 사용하여 리소스를 사용하도록 인증(로그인) 및 권한 부여(권한 있음)된 대상을 제어한다.

AWS 계정을 처음 생성하면, 모든 AWS 서비스 및 리소스에 대한 루트 사용자가 된다.(모든 서비스와 리소스에 대한 완전한 액세스 권한이 있는 ID)

루트 사용자는 사용하지 않는게 좋다. 루트 사용자는 각 서비스 및 리소스에 권한을 가지고 있는 다른 사용자를 만들어서 사용한다. 이 사용자들이 IAM 사용자이다.

루트 사용자 자격 증명을 안전하게 보관해 두고 몇 가지 계정 및 서비스 관리 작업을 수행할 때만 해당 자격 증명을 사용한다.

yml 파일 분리 vs 하나의 yml 파일

팀 별로 다르다. 하나의 yml파일만을 사용한다면, 이 파일만 관리하면 되지만, 새로운 환경이 추가될 때 빼먹을 수도 있다. yml을 분리한다면 이런 부분은 방지 할 수 있다.

테이블에 새로운 필드가 추가되는 경우

1) 테이블의 칼럼을 추가해준다.
2) 엔티티를 수정한 애플리케이션을 배포한다.
3) 배치 작업 or 수동으로 추가된 칼럼 값을 기존 데이터에 넣어준다.(테이블의 크기가 크다면, 서비스 특성상 부하가 적은 시간대에 모니터링과 함께 진행한다.)

책임 연쇄 패턴

명령 객체와, 처리 객체로 이루어진 패턴이다. 여러 처리 객체는 체인으로 연결되고, 각 처리 객체는 차례대로 명령을 수행한다. 수행할 수 없는 명령은 체인으로 연결되어 있는 다음 처리 객체로 넘겨준다.(다음 객체로 책임을 넘겨준다.)

요청을 보내는 객체와, 처리하는 객체간의 결합을 느슨하게 하기 위해 만들어진 패턴이다. (연관된 처리 방식의 결합을 느슨하게 유지). 외부에서는 어떤 방식으로 구현되어 있는지 알 필요가 없다.(OCP)

참고
책임 연쇄 패턴(chain-of-responsibility pattern)

예시

지하철의 거리별 추가 요금이 아래와 같이 적용된다고 하자.

  • 1) ~ 10km : 1250원
  • 2) 10km ~ 50km : 5km당 100원
  • 3) 50km ~ : 8km당 100원

이런 경우 만약 60km를 이동했다면, 10km까지는 1250원, 11km ~ 50km 까지는 5km당 100원이므로 800원(8 x 100), 51km ~ 60km까지는 8km당 100원이므로 200원(2 x 100)

즉, 1), 2), 3) 의 경우가 모두 적용되어야한다. 이런 경우 책임 연쇄 패턴을 적용해 볼 수 있다.

// 명령 객체

public class ChainOfDistance {
    private static final int FIRST_THRESHOLD = 10;
    private static final int SECOND_THRESHOLD = 50;

    private final DistanceChain firstChain;

    public ChainOfDistance() {
        DistanceChain third = new ThirdDistance();
        DistanceChain second = new SecondDistance(third, SECOND_THRESHOLD - FIRST_THRESHOLD);
        this.firstChain = new DefaultDistance(second, FIRST_THRESHOLD);
    }

    public int calculate(int distance) {
        return firstChain.calculate(distance);
    }
}
// 처리 객체 1
public class DefaultDistance implements DistanceChain {
    public static final int BASIC_FARE = 1250;

    private final DistanceChain chain;
    private final int threshold;

    public DefaultDistance(DistanceChain nextChain, int threshold) {
        this.chain = nextChain;
        this.threshold = threshold;
    }

    @Override
    public int calculate(int distance) {
        if (distance <= threshold) {
            return BASIC_FARE;
        }
        return BASIC_FARE + this.chain.calculate(distance - threshold);
    }
}
// 처리 객체 2
public class SecondDistance implements DistanceChain {
    private static final int UNIT = 5;
    private static final int UNIT_FARE = 100;

    private final DistanceChain chain;
    private final int threshold;

    public SecondDistance(DistanceChain nextChain, int threshold) {
        this.chain = nextChain;
        this.threshold = threshold;
    }

    @Override
    public int calculate(int distance) {
        if (distance > threshold) {
            return calculate(threshold) + this.chain.calculate(distance - threshold);
        }

        int count = (distance - 1) / UNIT;
        return (int) ((Math.ceil(count) + 1) * UNIT_FARE);
    }
}
// 처리 객체 3
public class ThirdDistance implements DistanceChain {
    private static final int UNIT = 8;
    private static final int UNIT_FARE = 100;

    @Override
    public int calculate(int distance) {
        int count = (distance - 1) / UNIT;
        return (int) ((Math.ceil(count) + 1) * UNIT_FARE);
    }
}

무중단 배포

버전 변경이 있을 때, 운영중인 서비스를 중지시키지 않고, 버전을 업데이트하는 방법.

블루 그린 배포

이전 버전(blue)에 있던 사용자 트래픽을 새 버전(green)으로 점진적으로 이전하는 방법이다. 실제로 하나의 서버를 더 띄워두고(동일하게 구성된 환경) 일시에 라우팅을 돌리는 방법을 뜻한다.

blue와 green 모두 프로덕션 환경에서 실행상태를 유지한다. 로드밸런서를 사용하여 트래픽을 blue에서 green으로 이전시킨다. 프로덕션 트래픽이 blue에서 green으로 완전히 이전되면 blue는 롤백에 대비하여, 대기 상태로 두거나 다음 버전의 템플릿으로 사용할 수 있다.

롤링 배포

서버를 하나씩 이전 버전에서 새 버전을 교체해가는 전략이다. 서비스 중인 여러 서버 중 하나를 제외시키고, 그 자리에 새 버전의 서버를 추가해준다.(사용자 api 요청을 직접적으로 처리하지 않는 서버들부터 순차적으로 죽이고, 새로 띄우고를 반복해서 배포하는 방법.) 이런 식으로 이전 버전의 서버들을 점진적으로 새 버전으로 교체해준다.

서버 수의 제약이 있는 경우 유용한 방법이지만, 배포 중에 인스턴스의 수가 줄어들게 되기 때문에, 서버 처리 용량을 미리 고려해줘야 한다.

카나리 배포

이전 버전의 서버와 새 버전의 서버를 구성하고, 이전 버전의 일부 트래픽을 새 버전으로 분산시켜 오류여부를 판단하는 방법. 점차 새 버전의 트래픽을 높여가며 오류가 나지 않는다면 완전히 옮기고, 오류가 나면 이전 버전으로 돌릴 수도 있다.

참고
Blue-Green 배포란?
Blue-Green 배포 방식
배포 전략: Rolling, Blue/Green, Canary

0개의 댓글