Spring Boot 프로젝트(fly, AWS 배포) 정리

소비자우롱차·2024년 12월 16일

산대특 개념별 정리

목록 보기
11/11

참고

  1. 도메인 구매
  1. 도메인 설정 -> 관리

1강, Spring Boot 프로젝트(fly, AWS 배포), 프로젝트 세팅


2강, Spring Boot 프로젝트(fly, AWS 배포), todo CRUD 구현


3강, Spring Boot 프로젝트(fly, AWS 배포), run 메서드 크기 줄이기


4강, Spring Boot 프로젝트(fly, AWS 배포), 핵심기능 이전

  1. 하나의 메서드는 하나의 일만
  • 하나의 메서드가 너무 크면 별로야
  1. 하나의 클래스는 한가지 주제만
  • 하나의 클래스가 너무 크면 별로야
  1. 해결법 : 여러개의 작은 클래스와 작은 메서드로 나눠야함
    ========> 리팩토링
    TodoController
    SystemController

5강, Spring Boot 프로젝트, 프로젝트 세팅


6강, Spring Boot 프로젝트, 원격 메서드 호출

보는 위치역할
@Controller컨트롤러
해당 클래스의 객체를 스프링부트가 생성하고(1개만)
HTTP 요청을 처리할 때마다 사용하도록
@GetMapping메서드
해당 메서드를 브라우저를 통해서 호출 가능하도록
@ResponseBody메서드
해당 메서드의 return 값을 String 형태로 변환하여 응답 본문으로 설정하도록
  1. 운영환경(실제 서비스 환경)에서는 클라이언트(고객, 브라우저)와 서버(주인, 스프링부트)는 다른 컴퓨터에 실행된다.
  2. 클라이언트와 서버 사이에는 통신이 필요하다.
  3. 브라우저와 스프링부트 사이에는 이미 통신기능이 들어있다.
  • HTTP 방식으로 통신한다.
  1. 스프링부트 프로젝트 ~~Controller 를 고객과의 소통을 담당하는 컨트롤러로 만들어야 한다. -> @Controller
  2. 컨트롤러의 모든 메서드를 외부(브라우저)에서 호출(URL 호출)할 수 있도록 만들어야한다. -> X
  3. 특정 메서드를 외부에서 호출 가능한 상태로 만들려면? @GetMapping("경로") 추가
  • 컨트롤러 안에서 필요한 특정 메서드만 골라서 액션 메서드로 만들 수 있다.
  1. 고객의 요청(주소창에 입력, F5(새로고침))마다 액션 메서드가 실행된다.
  2. 액션 메서드가 수행되고, return값을 해당 요청에 대한 응답으로 화면에 표시되길 원한다면 -> @ResponseBody

  1. 스프링부트의 목적은 고객의 입장에서 보면 원격지원에 있다.
  • todoApp 처럼 고객이 직접 스프링부트가 실행되고 있는 컴퓨터로 와서 입력할 필요가 없다.
  1. @Controller, @GetMapping, @ResponseBody
  • 자바가 이해하는 주석 -> 스프링부트에게 개발자(나)의 의도를 알리기 위함

7강, Spring Boot 프로젝트, Http와 String

  1. HTTP는 편지를 이용해서 고객(브라우저)과 점원(컨트롤러)이 대화를 나누는 방법에 대한 규칙
  • 고객 == 클라이언트 == 브라우저 == 서비스 이용자
  • 점원(주인) == 서버 == 스프링부트 == 서비스 제공자
  1. 고객은 요청 편지를 점원에게 보내고, 점원은 그에 대한 응답 편지를 보낸다.
  2. 편지는 헤더와 바디로 나뉜다.
  • 헤더에는 부가정보, 바디에는 내용이 담긴다.
  1. 고객의 생각(데이터)이 점원에게 전해지려면, 양쪽이 모두 다 이해할 수 있는 String을 사용해야한다.
  • 브라우저(고객)의 모국어는 자바스크립트, 스프링부트(점원)의 모국어는 자바
    • 브라우저는 자바의 int, boolean, char, float, 배열, 리스트, 맵 ... 을 이해할 수 없다.
    • 스프링부트도 자바스크립트의 number, 객체... 를 이해할 수 없다.
    • 동시에 이해하는게 String이다.(공용어)-> 결론 : 그래서 return 타입으로 String을 사용했던 것

HTTP 통신과 편지통신의 비교

HTTP 통신편지로 통신
요청Request(요청)보낸편지
응답Response(응답)답장편지
요청자고객(클라이언트)보낸이
응답자주인(서버)받은이
편지내용(헤더)발신자 주소, 수신자 주소발신자 주소, 수신자 주소
편지내용(바디)핵심내용편지의 본문

자바스크립트와 자바의 자료형 비교

자바스크립트자바
숫자numberbyte, short, int, long, float, double
논리booleanboolean
문자열stringString
고정크기배열없음new int[50]
리스트배열([10, 20, 30])List(List.of(10, 20, 30))
객체({"age":23, "name": "Paul", "name-2": "Kang"})HashMap(Map.of("age", 23, "name", "Paul", "name-2", "Kang"))
문자numberchar

8강, Spring Boot 프로젝트, 여러가지 리턴타입, Jackson과 Json 파싱

케이스 1 : HTTP 요청 /somePath?age=1&active=true 에 의해서 액션 메서드 hello(int age, boolean) 가 호출될 때 잭슨이 작동하는 방법

케이스 2 : HTTP 요청에 의해서 호출된 액션메서드가 공용어인 String(Text) 이외에 다른 형태의 데이터를 리턴한 경우

XML과 JSON 비교

데이터 유형XML 예시JSON 예시
숫자<age>22</age>{"age": 22}
문자<name>Paul</name>{"name": "Paul"}
객체<person><age>22</age><name>Paul</name></person>{"age": 22, "name": "Paul"}
배열<ages><age>22</age><age>23</age></ages>[22, 23]
배열<객체><people><person><age>22</age><name>Paul</name></person><person><age>23</age><name>Alice</name></person></people>[{"age": 22, "name": "Paul"}, {"age": 23, "name": "Alice"}]

9강, Spring Boot 프로젝트, Lombok

유용한 어노테이션

어노테이션역할
@Getter모든 필드에 대한 getter 메서드를 자동으로 생성
@Setter모든 필드에 대한 setter 메서드를 자동으로 생성
@ToString클래스의 toString 메서드를 자동으로 생성. @ToString.Exclude로 특정 필드를 제외할 수 있음
@AllArgsConstructor모든 필드를 매개변수로 받는 생성자를 자동으로 생성
@Builder빌더 패턴을 구현. 객체 생성 시점에 설정해야 할 필드를 명시적으로 설정할 수 있도록 함
@JsonIgnoreJSON 직렬화/역직렬화 시 특정 필드를 무시하도록 지정
@EqualsAndHashCodeequals와 hashCode 메서드를 자동으로 생성. @EqualsAndHashCode.Include로 특정 필드만 포함 가능
@SneakyThrows메서드에 선언된 checked exception을 무시하고 런타임 예외로 처리하도록 함

10강, Spring Boot 프로젝트, 프로젝트 세팅 및 URL추가


11강, Spring Boot 프로젝트, URL 단축 방식 설정


12강, Spring Boot 프로젝트, 단축된 URL을 통해 원본 URL로 이동


13강, Spring Boot 프로젝트, 도커(docker)

핵심

  1. 도커 이미지는 밀키트와 비슷하지만 훨씬 더 편리합니다.
  • 비유하자면, 밀키트는 밀키트인데 조리도구(가스레인지, 냄비 등)까지 포함된 밀키트라고 보시면 됩니다.
  • 그래서 현대의 웹 서비스에서 주류기술로 자리잡았습니다.
  1. 컨테이너(Container)
  • 컨테이너는 애플리케이션과 그 필요한 모든 것을 포함하는 격리된 환경입니다.
  • 가볍고 빠르게 시작되며, 다른 컨테이너나 호스트 시스템과의 충돌 없이 독립적으로 실행됩니다.
  • 이는 개발, 배포, 실행을 일관되게 만들어 줍니다.
  1. 이미지(Image)
  • 이미지는 컨테이너를 생성하는 데 사용되는 템플릿으로, 애플리케이션 실행에 필요한 코드, 라이브러리, 환경설정 등이 포함됩니다.
  • 읽기 전용이며, 컨테이너는 이 이미지를 기반으로 생성됩니다.
  1. 도커 데몬(Docker Daemon)
  • 도커 데몬은 컨테이너의 생성, 실행, 모니터링, 삭제 등을 관리하는 백그라운드 프로세스입니다.
  • 도커 클라이언트와 통신하며, 도커의 모든 중요 작업을 담당합니다.
  1. 도커 파일(Dockerfile)
  • 도커 파일은 도커 이미지를 빌드하기 위한 설정 파일로, 이미지 생성 과정에 필요한 명령어를 순서대로 담고 있습니다.
  • 이 파일을 사용하면 이미지 빌드 과정을 자동화하고, 재사용 가능한 방식으로 관리할 수 있습니다.
  1. 도커 허브(Docker Hub)
  • https://hub.docker.com/
  • 도커 허브는 도커 이미지를 저장하고 공유할 수 있는 클라우드 서비스입니다.
  • 사용자는 자신의 이미지를 업로드하여 공유할 수 있으며, 다른 사람이 만든 이미지를 검색하고 사용할 수 있습니다.

14강, Spring Boot 프로젝트, 도커 데스크톱 설치(docker desktop)

모든 도커자원(이미지, 컨테이너, 네트워크 볼륨) 삭제 명령어

  • 가끔 도커 관련 실습을 처음부터 다시하고 싶을 때 아래 명령어가 유용합니다.
  1. 컨테이너 삭제

    docker rm -f $(docker ps -qa)
  2. 이미지 삭제

    docker rmi -f $(docker images -qa)
  3. 안쓰는 네트워크 삭제

    docker network prune -f
  4. 안쓰는 볼륨 삭제

    docker volume prune -f

자주 발생하는 도커설치 관련 에러 1 : 윈도우에서 도커 데스크톱 설치시, Hardware assisted virtualization and data execution protection must be enabled in the BIOS

  1. 오류
    Hardware assisted virtualization and data execution protection must be enabled in the BIOS
  2. 해결책

15강, Spring Boot 프로젝트, 도커 이미지 만들기

Docker 사용해보기

  1. 일단 Git Bash 열고 아래를 순차적으로 실행

1) mkdir projects -> projects 폴더 생성
2) cd projects/ -> projects 경로로 이동
3) mkdir docker-project-1 -> docker-project-1 폴더 생성
4) cd docker-project-1/ -> docker-project-1 경로로 이동
5) vim Dockerfile 입력 후 a나 i를 눌러서 INSERT 상태로 만들고 GPT에게 물어보기 - 1의 A.에 해당하는 답변 복사해서 ESC -> :wq! -> Enter 입력해서 저장
6) ls 입력해서 Dockerfile 뜨는지 확인
7) cat Dockerfile 입력해서 5) 저장했던 내용이 뜨는지 확인
8) vim index.html 입력 후 a나 i를 눌러서 <h1>Hello</h1> 등 아무 태그나 입력 후에 ESC -> :wq! -> Enter 입력해서 저장
9) ls 입력해서 Dockerfile, index.html 뜨는지 확인
10) cat index.html 입력해서 8) 저장했던 내용이 뜨는지 확인
11) docker build -t 이미지 이름 . ex) docker build -t nginx-1 . 하면 이미지가 저장된다 -> .(콤마)는 현재 폴더라는 뜻
12) docker images 입력 후 아래와 같이 뜨는지 확인 REPOSITORY, TAG, IMAGE ID, CREATED, SIZE가 각각 뜨는지 확인

  1. GPT에게 물어보기 - 1
  • Q. index.html이 있는데 그걸 실행할 nginx가 필요해. 이 모든것을 다 담은 docker file 만들어
  • A.
# Nginx 이미지를 베이스로 사용
FROM nginx:latest

# 로컬의 index.html 파일을 Nginx 기본 경로로 복사
COPY index.html /usr/share/nginx/html/index.html

# Nginx가 사용하는 기본 포트 80을 공개
EXPOSE 80

# Nginx 실행
CMD ["nginx", "-g", "daemon off;"]
  1. GPT에게 물어보기 - 2
  • Q. 지금 폴더에 index.html이 있고 Dockerfile도 있는데, 여기서 이름이 nginx-1인 도커 이미지를 만드는 명령어 내놔
  • A.
docker build -t nginx-1 .

16강, Spring Boot 프로젝트, 도커 이미지 삭제

  1. docker rmi 이미지 ID 입력하면 지워진다. -> 이미지 ID가 똑같은 경우에는 이름(REPOSITORY)으로 지워야한다.

  2. 도커 이미지 삭제

  • docker rmi 이미지 ID or docker rmi 이미지 이름
  • 도커 이미지 이름 : 리포지터리_이름 : 태그
    • 기본 태그는 latest
    • 기본 태그는 생략 가능
      • ex) docker rmi nginx-1:latest == docker rmi nginx-1

17강, Spring Boot 프로젝트, 도커 컨테이너 생성 및 삭제

  1. 도커 이미지로 도커 컨테이너 생성(도커 이미지를 실행하면 도커 컨테이너가 생성된다.)
  • 도커 이미지는 일종의 템플릿(이력서의 양식)
  • 도커 컨테이너는 도커 이미지를 디스크에서 복사해서 메모리에 띄운 것이다.
    • 이력서를 작성을 해야해 -> 이력서 양식 다운로드 -> 원본에서 작업하지 않고, 복사본을 만들고 작업하는 것과 비슷함.
  • 80 포트를 사용하는 nginx-1-1 컨테이너를 띄운다.
    • docker run -d -p 80:80 --name nginx-1-1 nginx-1 --> 다른 컨테이너에서 80 사용 불가능, nginx-1-1 사용 불가능
  • 8081 포트를 사용하는 nginx-1-2 컨테이너를 띄운다.
    • docker run -d -p 8081 :80 --name nginx-1-2 nginx-1 --> 다른 컨테이너에서 8081 사용 불가능, nginx-1-2 사용 불가능
  • 1개의 도커 이미지로 2개의 컨테이너를 띄웠고(같은 이름 불가, 같은 포트 불가라서 서로 다른 이름과 포트를 할당)
  • 도커 컨테이너 삭제
    • docker rm -f 컨테이너_이름 --> ex) docker rm -f nginx-1-1

18강, Spring Boot 프로젝트, flyctl 설치

  1. fly.io는 서버(PC)를 제공해준다.
  • 서버 == 컴퓨터
  • 서버에는 공인 IP가 부여되어 있다.
  • 일반적으로 웹 서버에는 도메인(IP의 별칭)이 부여되어 있다.
    • 웹 서버 : 서버 중에서 웹 서비스를 제공하는 서버
  • flyctl을 설치하면 컴퓨터에서 명령어로 fly.io를 이용할 수 있다.
    • flyctl을 설치하고 로그인해야 사용 가능

19강, Spring Boot 프로젝트, flyctl로 앱 생성하기

  1. fly.io에 서비스 배포를 위해서는 '앱'이 필요함
  • 앱은 fly.io에서의 서비스 단위
  • 내가 5개를 서비스 해야하면 앱이 5개 필요하다.
  • 앱은 fly launch --no-deploy 명령어로 생성 (PowerShell를 관리자 권한으로 열고 fly.io폴더가 있는 경로로 간 후)
    • 앱은 fly.io 대시보드에서 확인 가능
    • fly.toml 파일이 생성됨 -> 앱의 설정을 바꿀 때 사용
  • fly deploy 명령어 수행 전이기 때문에 앱은 현재 Pending(보류) 상태

20강, Spring Boot 프로젝트, fly.io 앱에 배포 테스트

  1. fly.io앱에 배포 테스트
  • 3가지 요소만 있으면 fly.io 배포 가능

    • 3가지 요소: 프로젝트 폴더, Dockerfile, fly.toml
    • fly.toml만 있으면 안되고, fly.toml에 대응하는 fly.io 앱이 있어야 함
  • 프로젝트 폴더에서 fly deploy 명령어 입력

    • 프로젝트 폴더의 모든 파일들이 fly.io 앱으로 복사
    • 빌더 앱이 생성되고 업로드 된 파일들로 도커 빌드 진행
    • 도커 이미지 완성
    • machines(보통 2개)에 컨테이너가 위치함 -여기서 machine은 머신, PC, 서버를 의미
  • 도메인은 https://앱_이름.fly.dev

  • 소스코드가 바뀌면 재배포 해야함

    • 재배포 명령어 : fly deploy or flyctl deploy
    • 머신에서 돌아가고 있는 기존 컨테이너는 제거되고 새 도커이미지로 만든 컨테이너가 차지
      • 기존 컨테이너가 모두 사라지고, 새 컨테이너가 그 자리를 차지하게 되면,
      • 단순 교체로 인한 고객입장에서의 중단 타임 발생
      • 고객 입장에서 웹서비스를 이용하는중에 404 를 볼 수 있다.
  • fly.io는 기본적으로 롤링 업데이트를 사용한다. -> 무중단배포(교체) 방식


21강, Spring Boot 프로젝트, 도커파일 생성 및 fly 배포

Dockerfile(자바 21, 스프링부트 배포용)

  1. 주의사항
  • 기존 Dockerfile은 작동하지 않습니다.
  • 아래의 Dockerfile을 사용해주세요.
# 첫 번째 스테이지: 빌드 스테이지
FROM gradle:jdk21-graal-jammy AS builder

# 작업 디렉토리 설정
WORKDIR /app

# 소스 코드와 Gradle 래퍼 복사
COPY build.gradle .
COPY settings.gradle .

# Gradle 래퍼에 실행 권한 부여
RUN gradle wrapper

# 종속성 설치
RUN ./gradlew dependencies --no-daemon

# 소스 코드 복사
COPY src src

# 애플리케이션 빌드
RUN ./gradlew build --no-daemon

# 두 번째 스테이지: 실행 스테이지
FROM container-registry.oracle.com/graalvm/jdk:21

# 작업 디렉토리 설정
WORKDIR /app

# 첫 번째 스테이지에서 빌드된 JAR 파일 복사
COPY --from=builder /app/build/libs/*.jar app.jar

# 실행할 JAR 파일 지정
ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=prod", "app.jar"]

22강, Spring Boot 프로젝트, 개발환경과 운영환경의 설정파일 분리

1. 간략한 정리

  • 개발 환경(dev)과 운영 환경(prod)은 다르다.
  • 각 환경에 맞는 설정 필요
  • 스프링부트가 지원해줌
  • application.yml은 모든환경에서 작동
    • application.yml 의 spring.profiles.active=dev 설정 때문에 작동함
    • application-prod.yml 은 운영환경
    • Dockerfile의 -Dspring.profiles.active=prod 옵션 때문에 작동함
  • application.yml이 제일 먼저 작동
    • 먼저 작동하는 설정파일이 우선순위가 낮다
    • 나중에 작동하는 설정파일의 정보가 우선 적용됨
  • 프로젝트 폴더에서 fly deploy 명령 입력 -> 바뀐 소스코드, 설정을 실서버(운영서버)에 반영

23강, Spring Boot 프로젝트, CI/CD 구축

1. 간략한 정리

  • 깃허브 리포지터리에 푸시하는 것 만으로 자동으로 배포까지 이루어질 수 있도록

    • CI/CD 라고 한다.
      • fly.io가 무중단이기 때문에 결과적으로는 무중단 CI/CD가 걸려있게 된다.
  • GITHUB ACTION을 걸어두면 특정 이벤트가 발생했을 때 어떤 일이 작동하도록 할 수 있다.

    • 이벤트 예시 : 특정 브랜치의 특정 파일이 변경되는 푸시가 발생.
  • 소스코드의 내용 중 Github에 노출되면 안되는 내용

    • 이런 것들은 application-secret.yml에 몰아넣고
    • gitignoresrc/main/resources/application-secret.yml을 추가
    • application-secret.yml은 모든 환경에서 작동
      • application.yml의 spring.profiles.include=secret 설정 때문
  • 목표는 도커 빌드를 fly.io에서 수행하기

    • 빌드 == 완제품 만들기
    • 빌드를 할 때 application-secret.yml이 필요하다.
    • github 리포지터리에는 application-secret.yml가 있어야 한다.
    • 나는 application-secret.yml를 로컬에만 존재해도록 해놨다. -> 보안 때문에
  • application-secret.yml를 암호화해서 해당 리포지터리에 넣어야한다.

    • github 시크릿 변수 기능 활용.
    • application-secret.yml 내용을 시크릿 변수에 저장을 하면 된다.
  • .github/workflows/deploy.yml 파일 추가

    • .github/workflows 까지의 경로는 지켜야한다. -> 이렇게 해야 이벤트가 걸린다.
    • deploy.yml에는 언제 해당 파일이 실행되어야 하는지, 실행되면 무슨 일이 일어나야 하는지 기술되어있다.
  • 리포지터리 세팅을 해야한다.

    • APPLICATION_SECRET_YML 생성
    • FLY_API_TOKEN 생성
      • fly.io에 create AccessToken으로 발급 받으면 된다.
    • Settings -> Actions -> General -> Workflow permissions -> Read and write permissions 선택하면 된다.
      • 추후 AWS 배포를 위한 설정
      • fly.io에서는 필요하지 않다.
      • GITHUB ACTION이 해당 리포지터리에 태그나 릴리즈를 생성할 권한을 얻는다.
  • Github Push

    • 이제는 fly deploy를 따로 할 필요가 없다
    • push하면 자동으로 배포까지 진행
      • 무중단 CI/CD가 완성!

2. .gitignore

HELP.md
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/

### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/

### VS Code ###
.vscode/

### Custom ### <--- 추가된 부분
src/main/resources/application-secret.yml
db_dev.mv.db
db_dev.trace.db
src/main/generated

3. .github/workflows/deploy.yml

name: Fly Deploy
on:
  push:
    paths:
      - settings.gradle
      - build.gradle
      - src/**
      - fly.toml
      - Dockerfile
      - .github/workflows/deploy.yml
    branches:
      - main    --> 실제 브랜치명으로 변경
jobs:
  deploy:
    name: Deploy app
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: application-secret.yml 생성
        env:
          APPLICATION_SECRET: ${{ secrets.APPLICATION_SECRET_YML }}
        run: echo "$APPLICATION_SECRET" > src/main/resources/application-secret.yml
      - uses: superfly/flyctl-actions/setup-flyctl@master
      - run: flyctl deploy --remote-only
        env:
          FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}

24강, Spring Boot 프로젝트, 현재 구축된 CI/CD 주의사항

1. 간략한 정리

  • 소스코드 변경시에 실서버에 반영하려면?
    • push만 하면 됨
  • 만약에 application-secret의 내용이 바뀌고, 그것을 서버에 반영하려면?
    • 리포지터리 내의 시크릿 변수를 수정해야함 -> push or 해당 액션 re-run
  • 앱 테스트 -> SurlController 참고
  • fly.io의 기본적인 앱은 일정시간동안 요청이 없으면 자동으로 꺼진다.
    • 스프링부트 내부에 있는 변수에 저장된 정보들이 전부 날아간다. -> 영속성을 부여해서 해결
    • 필요없는 상황에서 자동으로 앱이 꺼지기 때문에 비용 절감이 됨

25강, Spring Boot 프로젝트, MySQL 설치(Docker), DBeaver 설치

1. MySQL 서버 띄우기

  • MySQL 포트는 기본적으로 3306 을 사용합니다.
  • 설치가 안된다면 기존에 직접 설치한 MySQL 을 지우고 다시시도해주세요.
  • root 계정의 비밀번호는 koreait123414 로 하겠습니다.
# MySQL 없을 때 띄우는 방법
cd ~ # 운영환경에서는 `cd /`

# 설정파일 만들기
mkdir -p dockerProjects/mysql-1/volumes/etc/mysql/conf.d

# 원하는 설정을 적어주세요.
chmod 644 dockerProjects/mysql-1/volumes/etc/mysql/conf.d/my.cnf
echo "[mysqld]
# general_log = ON
# general_log_file = /etc/mysql/conf.d/general.log" > dockerProjects/mysql-1/volumes/etc/mysql/conf.d/my.cnf
chmod 444 dockerProjects/mysql-1/volumes/etc/mysql/conf.d/my.cnf

docker run \
    --name mysql-1 \
    -p 3307:3306 \
    -v /${PWD}/dockerProjects/mysql-1/volumes/var/lib/mysql:/var/lib/mysql \
    -v /${PWD}/dockerProjects/mysql-1/volumes/etc/mysql/conf.d:/etc/mysql/conf.d \
    -e TZ=Asia/Seoul \
    -e MYSQL_ROOT_PASSWORD=koreait123414 \
    -d \
    mysql:8.4.1

2. MySQL 초기화

  • MySQL 서버를 초기화할 때 사용해주세요.
# MySQL 없을 때 띄우는 방법
cd ~ # 운영환경에서는 `cd /`

# 기존 컨테이너와 볼륨 제거
docker ps -a | grep -q mysql-1 && docker rm -f mysql-1
rm -rf dockerProjects/mysql-1

# 설정파일 만들기
mkdir -p dockerProjects/mysql-1/volumes/etc/mysql/conf.d

# 원하는 설정을 적어주세요.
echo "[mysqld]
# general_log = ON
# general_log_file = /etc/mysql/conf.d/general.log" > dockerProjects/mysql-1/volumes/etc/mysql/conf.d/my.cnf
chmod 444 dockerProjects/mysql-1/volumes/etc/mysql/conf.d/my.cnf

docker run \
    --name mysql-1 \
    -p 3307:3306 \
    -v /${PWD}/dockerProjects/mysql-1/volumes/var/lib/mysql:/var/lib/mysql \
    -v /${PWD}/dockerProjects/mysql-1/volumes/etc/mysql/conf.d:/etc/mysql/conf.d \
    -e TZ=Asia/Seoul \
    -e MYSQL_ROOT_PASSWORD=koreait123414 \
    -d \
    mysql:8.4.1

3. 모든 도커자원(이미지, 컨테이너, 네트워크 볼륨) 삭제 명령어

  • 가끔 도커 관련 실습을 처음부터 다시하고 싶을 때 아래 명령어가 유용합니다.
# 컨테이너 삭제
docker rm -f $(docker ps -qa)

# 이미지 삭제
docker rmi -f $(docker images -qa)

# 안쓰는 네트워크 삭제
docker network prune -f

# 안쓰는 볼륨 삭제
docker volume prune -f

4. Dbeaver 최신 버전 설치 및 세팅

  1. Dbaver 최신버전 다운로드
  • 최신버전에서 F9(스크립트 실행)관련 오류가 있을 수 있습니다.
  • 만약에 오류가 있다면 아래 링크에 있는 23.3.5 버전을 받아주세요.

  1. Dbeaver 23.3.5 버전 설치 및 세팅
  • 최신버전이 안될때는 최신버전을 삭제 후 이 버전으로 설치해보세요.
  • Dbaver 23.3.5 버전 다운로드
    • macos-aarch64.dmg : 맥 M1, M2 칩
    • macos-x86_64.dmg : 맥 인텔칩 칩
    • macos-x86_64-setup.exe : 윈도우 사용자

26강, Spring Boot 프로젝트, JPA 의존성 추가

1. 간략한 정리

  • ORM을 쓰면 개발자가 SQL을 직접 작성하지 않고 관련 작업을 JAVA 코드로 수행 가능
  • ORM이 개발자 대신 상황에 맞는 SQL을 생성하고 실행(JPA만, MyBatis는 직접 짜야함)
  • Spring Data JPA는 가장 유명하고 유용한 ORM
  • Spring Data JPA는 데이터 소스를 통해서 본인(ORM, 여기서는 JPA)이 다뤄야 하는 실제 DB에 대한 정보를 얻는다.
  • 설정 파일(.yml)에서 아래 4가지는 반드시 있어야 작성해야한다.
    • url
    • username
    • password
    • driver-class-name

27강, Spring Boot 프로젝트, Article 클래스로 article 테이블 생성

1. 간략한 정리

  • @Entity : 이 클래스로 테이블 만들겠다.
  • @Id : 이 필드를 PK(Primary Key) 만들거야.
  • @GeneratedValue(strategy = IDENTITY) : AUTO_INCREMENT 할거야.
  • @Column(columnDefinition = "TEXT") : 타입을 TEXT로 할거야.

28강, Spring Boot 프로젝트, JPA가 실행하는 SQL 로깅


29강, Spring Boot 프로젝트, 게시글 INSERT


30강, Spring Boot 프로젝트, COUNT, DELETE, TRUNCATE


31강, Spring Boot 프로젝트, 게시글 UPDATE

1. 간략한 정리

  • MySQL 초기화(제너럴 로그 켠 모드로 시작)
  • echo '' 으로 로그 비우는 방법
  • cat과 less로 으로 로그 확인하는 방법
  • 기존의 코드는 트랜잭션이 3개로 구성됨
  • 하나의 트랜잭션으로 묶음
  • 트랜잭션 내에서의 엔티티 객체 수정은 자동으로 UPDATE가 유발됨
  • 영속성 컨텍스트는 트랜잭션 당 하나가 존재함
  • 모든 엔티티 객체는 영속성 컨텍스트에 복제본(스냅샷)이 존재
  • 영속컨 컨텍스트는 트랜잭션이 종료될 때 더티체킹에 의해 체크된 것들에 대해서 UPDATE

2. MySQL 초기화(제너럴 로그 켠 모드로 시작)

  • 이렇게 시작하면 MySQL 에서 실행된 모든 쿼리로그를 general.log 에서 볼 수 있습니다.
  • general.log 를 보면 트랜잭션이 어떻게 묶이는지도 확인할 수 있습니다.
# MySQL 없을 때 띄우는 방법
cd ~ # 운영환경에서는 `cd /`

# 기존 컨테이너와 볼륨 제거
docker ps -a | grep -q mysql-1 && docker rm -f mysql-1
rm -rf dockerProjects/mysql-1

# 설정파일 만들기
mkdir -p dockerProjects/mysql-1/volumes/etc/mysql/conf.d

# 원하는 설정을 적어주세요.
echo "[mysqld]
general_log = ON
general_log_file = /etc/mysql/conf.d/general.log" > dockerProjects/mysql-1/volumes/etc/mysql/conf.d/my.cnf
chmod 444 dockerProjects/mysql-1/volumes/etc/mysql/conf.d/my.cnf

docker run \
    --name mysql-1 \
    -p 3306:3306 \
    -v /${PWD}/dockerProjects/mysql-1/volumes/var/lib/mysql:/var/lib/mysql \
    -v /${PWD}/dockerProjects/mysql-1/volumes/etc/mysql/conf.d:/etc/mysql/conf.d \
    -e TZ=Asia/Seoul \
    -e MYSQL_ROOT_PASSWORD=lldj123414 \
    -d \
    mysql:8.4.1

3. 제너럴 로그 초기화 및 확인

# 이동
cd ~ # 운영환경에서는 `cd /`
echo '' > dockerProjects/mysql-1/volumes/etc/mysql/conf.d/general.log
cat dockerProjects/mysql-1/volumes/etc/mysql/conf.d/general.log | less # 방향키 위/아래 로 이동, 종료는 q

32강, Spring Boot 프로젝트, 게시글 SELECT


33강, Spring Boot 프로젝트, 여러가지 SELECT


34강, Spring Boot 프로젝트, 엔티티의 생성,수정날짜 자동기입


35강, Spring Boot 프로젝트, Service 도입


36강, Spring Boot 프로젝트, RsData 도입


37강, Spring Boot 프로젝트, 샘플 회원 2명 생성


38강, Spring Boot 프로젝트, 오류 상태에서 리턴이 아닌 예외발생

1. GlobalException 클래스 추가

@Getter
public class GlobalException extends RuntimeException {
    private final RsData<Empty> rsData;
    public GlobalException() {
        this("400-0", "에러");
    }
    public GlobalException(String msg) {
        this("400-0", msg);
    }
    public GlobalException(String resultCode, String msg) {
        super("resultCode=" + resultCode + ",msg=" + msg);
        this.rsData = RsData.of(resultCode, msg);
    }
    public static class E404 extends GlobalException {
        public E404() {
            super("404-0", "데이터를 찾을 수 없습니다.");
        }
    }
}

39강, Spring Boot 프로젝트, 서비스에 @Transactional 적용

1. @Transactional

  1. @Transactional : 서비스의 모든 public 메서드에는 붙는게 관례, private 메서드에는 적용되지 않는다.
  2. @Transactional(readOnly = true) : 그 중에서 SELECT만 할거 같을 경우
  3. 1, 2가 귀찮으므로 클래스 수준에서 @Transactional을 붙인다.
  4. @Transactional이 붙은 메서드에서 @Transactional이 붙은 메서드를 실행하는 경우
  • 물리 트랜잭션은 가장 바깥쪽의 메서드 기준으로 1개만 작동한다.
  • 다만 논리 트랜잭션은 @Transactional가 붙은 메서드가 호출될 때마다 작동한다.
  • 물리 트랜잭션이 중요
  • 다만 논리 트랜잭션 안에서 RuntimeException 계열의 예외가 발생하면 조용한 롤백이 일어난다.
    • 이럴경우에는 try catch로 예외가 발생하는 메서드를 감싸도 소용이 없다.
  1. @Transactional(noRollbackFor = GlobalException.class)로 특정 예외에 대해 조용한 롤백이 되지 않도록 할 수 있다.

40강, Spring Boot 프로젝트, @MappedSuperclass


41강, Spring Boot 프로젝트, Article에 Member 추가

1. 간략한 정리

  • Article class에 작성자 필드 추가
  • private String username; 가능
  • private Long authorId; 가능
  • @ManyToOne private Member author 가 관례(JPA)

42강, Spring Boot 프로젝트, Surl 엔티티화


43강, Spring Boot 프로젝트, Surl에 author 필드 추가, 프록시


44강, Spring Boot 프로젝트, PostgreSQL 띄우기

1. MySQL 초기화(제너럴 로그 끈 모드로 시작)

  • 이렇게 시작하면 MySQL 에서 실행된 모든 쿼리로그를 general.log 에서 볼 수 있습니다.
  • general.log 를 보면 트랜잭션이 어떻게 묶이는지도 확인할 수 있습니다.
# MySQL 없을 때 띄우는 방법
cd ~ # 운영환경에서는 `cd /`

# 기존 컨테이너와 볼륨 제거
docker ps -a | grep -q mysql-1 && docker rm -f mysql-1
rm -rf dockerProjects/mysql-1

# 설정파일 만들기
mkdir -p dockerProjects/mysql-1/volumes/etc/mysql/conf.d

# 원하는 설정을 적어주세요.
echo "[mysqld]
# general_log = ON
# general_log_file = /etc/mysql/conf.d/general.log" > dockerProjects/mysql-1/volumes/etc/mysql/conf.d/my.cnf
chmod 444 dockerProjects/mysql-1/volumes/etc/mysql/conf.d/my.cnf

docker run \
    --name mysql-1 \
    --restart unless-stopped \
    -p 3307:3306 \
    -v /${PWD}/dockerProjects/mysql-1/volumes/var/lib/mysql:/var/lib/mysql \
    -v /${PWD}/dockerProjects/mysql-1/volumes/etc/mysql/conf.d:/etc/mysql/conf.d \
    -e TZ=Asia/Seoul \
    -e MYSQL_ROOT_PASSWORD=koreait123414 \
    -d \
    mysql:8.4.1

2. PostgreSQL 서버 삭제하는 방법

cd ~ # 운영환경에서는 `cd /`

docker rm -f postgres_1

rm -rf /${PWD}/dockerProjects/postgres_1

3. PostgreSQL 서버 띄우기

  • PostgreSQL 포트는 기본적으로 5432 입니다.
  • 설치가 안된다면 기존에 직접 설치한 PostgreSQL 을 지우고 다시시도해주세요.
  • postgres 계정의 비밀번호는 koreait123414 입니다.
cd ~ # 운영환경에서는 `cd /`

docker run \
    --name postgres_1 \
    --restart unless-stopped \
    -e POSTGRES_PASSWORD=koreait123414 \
    -v /${PWD}/dockerProjects/postgres_1/volumes/var/lib/postgresql/data:/var/lib/postgresql/data \
    -p 5432:5432 \
    -d postgres

4. PostgreSQL 상식

  • MySQL과 달리 PostgreSQL 에서는 관리자 계정이 root 가 아닌 postgres 이다.
  • postgres DB는 기본적으로 존재한다.
  • `CREATE DATABASE app1;`` 으로 데이터베이스를 만들 수 있다.
  • SELECT datname FROM pg_database; 로 데이터베이스 목록을 볼 수 있다.
  • MySQL 에서의 use 와 같은 명령어는 없다.

5. 현재 선택된 데이터베이스안의 모든 사용자 정의 테이블 목록 나열

SELECT tablename
FROM pg_catalog.pg_tables
WHERE schemaname != 'pg_catalog'
AND schemaname != 'information_schema';

6. post 테이블 생성

create table post (
    id bigserial not null,
    primary key (id),
    create_date timestamp(6),
    modify_date timestamp(6),
    title varchar(255)
)

7. post 테이블에 데이터 삽입

# V1
insert into post
(id, create_date, modify_date, title)
values
(default, NOW(), NOW(), '제목 1')
# v2
insert into post
(create_date, modify_date, title)
values
(NOW(), NOW(), '제목 1')

8. 검색어가 당근 이고, 1 페이지에 보여줄 글 가져오기

  • 한 페이지에 최대 10개의 글 노출 가능

데이터 쿼리

select P.\*
from post AS P
where upper(P.title) like upper('%당근%') escape '\'
order by P.id desc
limit 10 offset 0;
카운트 쿼리
select count(P.id)
from post AS P
where upper(P.title) like upper('%당근%') escape '\

카운트 쿼리

select count(P.id)
from post AS P
where upper(P.title) like upper('%당근%') escape '\'

9. 검색어가 당근 이고, 2 페이지에 보여줄 글 가져오기

  • 한 페이지에 최대 10개의 글 노출 가능

데이터 쿼리

select P.\*
from post AS P
where upper(P.title) like upper('%당근%') escape '\'
order by P.id desc
limit 10 offset 10;
카운트 쿼리
select count(P.id)
from post AS P
where upper(P.title) like upper('%당근%') escape '\'

카운트 쿼리

select count(P.id)
from post AS P
where upper(P.title) like upper('%당근%') escape '\'

45강, Spring Boot 프로젝트, PostgreSQL로 변경

🔎 간략한 정리

  1. PostgreSQL에는 MySQL과 다르게 use가 없기 때문에 아래 사진과 같이 해줘야한다.
  • PostgreSQL 마우스 우클릭 -> Edit Connection 클릭
  • Database란에 해당 Database 입력 후 OK 버튼 클릭
  1. build.gradle에서 Edit Starters... 를 클릭해서 아래 사진과 같이 해주면 된다.

  2. application.yml의 datasource를 바꿔줘야한다. PostgreSQL로

  • 아래 사진과 같이 바꿔주면 된다. (PostgreSQL의 포트 기본값은 5432이다.)

46강, Spring Boot 프로젝트, H2 DB

🔎 간략한 정리

  1. H2 Database : MySQL, PostgreSQL 보다 가볍다.
  • 메모리 모드 : 더 가벼워지기 때문에 테스트하기 좋다.
  1. H2 Database 사용
  • build.gradle dependencies에 아래 사진과 같은 코드 추가
  • application.yml의 datasource 부분을 아래 사진과 같이 수정
  • Chrome 브라우저 URL 창에 http://localhost:8070/h2-console 입력(8070에는 해당하는 포트번호를 넣어주기)하면 아래와 같이 뜨고, 빨간 박스 안에 application.yml의 datasource의 url을 넣어주고 connect 클릭
  • 아래와 같이 뜨면 완료
  • 주의사항 : 해당하는 프로젝트를 Run하지 않으면 사용 불가.

47강, Spring Boot 프로젝트,

1. fly.io에 PostgreSQL 생성

  • 프로젝트 폴더와 상관없는 곳에서 수행
flyctl pg create
  • 앱 이름 : cwy-surl-project-db
    • 이 앱 이름(cwy-surl-project-db)을 사용하시면 안됩니다.
    • 앱은 고유의 이름을 사용해야한다.

2. 만들어진 정보 저장(두번 다시 못본다.)

  • 그렇기에 따로 저장해놔야한다.
Postgres cluster ygc-surl-project-db created
  Username:    postgres
  Password:    u3Ge4ShhIGXVjWw
  Hostname:    ygc-surl-project-db.internal
  Flycast:     fdaa:b:1651:0:1::2
  Proxy port:  5432
  Postgres port:  5433
  Connection string: postgres://postgres:u3Ge4ShhIGXVjWw@ygc-surl-project-db.flycast:5432

Save your credentials in a secure place -- you won't be able to see them again!

3. 외부에서는 fly.io 안쪽의 db에 바로접근이 불가능하기 때문에, 접근이 필요할 때 마다 flyctl proxy 명령으로 개구멍을 open

  • flyctl proxy 5433:5432 -a appName
    • ex) flyctl proxy 5433:5432 -a ygc-surl-project-db
    • 로컬 포트 5433 을 원격지 포트 5432 와 연결한다.
      • 이렇게 하면 로컬에서의 포트 5432 와 충돌하지 않는다.
  • Ctrl + c 로 프록시 종료

4. Dbeaver 로 DB 접속

  • 프로젝트 이름 : ygc-surl-project-db, fly.io
  • 서버이름 : surl_prod
  • 주소 : 127.0.0.1
  • 사용자 : postgres
  • 비밀번호 :
  • 포트 : 5433
  • DB : postgres
    • 추후 surl_prod 로 변경

5. surl_prod 데이터베이스 생성

DROP DATABASE IF EXISTS surl_prod;
CREATE DATABASE surl_prod;

6. Dbeaver 로 DB 접속정보에서 DB변경

  • DB : surl_prod

7. fly.io 대시보드에서 확인


48강, Spring Boot 프로젝트, 운영 모드에서 운영 DB 사용

🔎 간략한 정리

  • application-prod.yml에 운영모드용 설정
  • initAll 빈 등록
  • 로컬에서 잘 되는지 테스트
  • @ActiveProfiles("test")
  • 테스트 환경에서는 H2 메모리 모드로 설정
  • initAll 다음에 initNotProd 가 수행되도록 설정
  • log.debug는 운영모드에서 작동 x

49강, Spring Boot 프로젝트, 도메인 구매 및 연결

🔎 정리

  1. 로컬 : http://localhost:8070

  2. 운영 : http://ygc-surl-project-11.fly.dev

  3. 도메인 구매

  1. 도메인 설정 -> 관리
  1. 가비아에서 도메인을 검색 및 선택 -> 결제(타사 관리 누르고 dnszi 로그인 후 네임서버 5개 보이는거 입력)

  2. 프로젝트에 도메인 연결하는법

  • gitbash나 powershell 열고 아래 코드 참고해서 입력
fly certs add www.surl.ygcqwe.site # 추가
fly certs show www.surl.ygcqwe.site # 확인
  1. CNAME 추가하는 법
  • DNSZI 로그인하고 좌측 도메인 목록 - 전체보기 클릭

  • 수정할 도메인 클릭

  • CNAME 관리 클릭

  • 목적지(연결) 도메인에 ygc-surl-project-11.fly.dev 입력 후 CNAME 추가 클릭

🔎 요약

  1. ygc-surl-project-11.fly.dev 는 기본적으로 제공되는 운영 도메인 입니다.
  2. dnszi 는 도메인 관리 서비스
  3. 도메인 구매 사이트에서 도메인 구매하기
  4. 도메인 구매시 DNS 서버를 dnszi 로 설정
  5. fly certs add 새-도메인
  6. dnszi 에서 CNAME 설정

50강, Spring Boot 프로젝트, 도메인 추가, 자원 해제


51강, Spring Boot 프로젝트, AWS 가입, IAM 계정 생성 및 로그인

🔎 간략한 정리

  1. AWS : fly.io보다 자유도가 높고, 여러 서비스의 조합이다.
  • AWS 대표 핵심자원 3가지

    • IAM(인증, 인가에 관련된 기능)
      • 권한, 정책
      • 역할, 사용자
    • VPC(네트워크에 관련된 기능)
    • EC2(컴퓨터에 관련된 기능)
  • AWS 자원을 다루는 방법

    • AWS 콘솔 : 일일이 하나하나 클릭해서 하는 것
    • AWS CLI : 명령어로 하는 것
      • 일괄 처리 가능
      • AWS 콘솔에 비해서 효율적
  1. fly : 인프라를 편하게 작업 가능(편의성)
  2. 결론 : 배포 편하게 할거면 fly, 내 입맛대로 쓰고 싶으면 AWS

🔎 IAM 사용자 계정 생성 방법(루트 계정으로 가입을 한 상태여야한다.)

  1. 우측의 사용자 생성 버튼 클릭

  2. 사용자 이름에 'admin' 입력 후 빨간 박스 1, 2, 3번 순으로 해주면 된다.

  • admin이 IAM 계정으로 로그인할 때의 사용자 이름이 된다.
  1. 빨간박스 1번 선택 -> 빨간박스 2번(AdministratorAccess) 체크 후 우측 아래 다음 버튼 클릭

  2. 우측 아래 사용자 생성 버튼 클릭

  3. 우측 아래 사용자 목록으로 돌아가기 버튼 클릭

  4. 오른쪽 AWS 계정란에 '계정 별칭' -> '생성' 클릭

  5. 기본 별칭에 입력 후 '별칭 생성' 클릭

🔎 AWS - IAM 사용자로 로그인 하는법

  1. 빨간 박스 클릭

  2. IAM 사용자 선택 후 아까 설정해놓은 별칭 입력 -> 다음 버튼 클릭

  3. 사용자 이름에 admin 입력 -> 암호 입력 후 로그인 버튼 클릭

  • 계정별칭은 일부러 지워놨다.
  1. 로그인 완료

🔎 IAM 계정을 생성하고 사용하는 이유는?

  1. AWS ROOT 계정을 해킹당하면 피해가 크기 때문에 IAM 유저를 만들고 그걸 사용하는게 좋기 때문에
  2. ROOT 계정의 역할 : IAM USER admin을 만드는 역할을 할 뿐이고 그 외의 용도로는 사용하지 않는게 바람직하다.
  3. IAM에서는 사용자의 편의를 위한 정책들을 미리 만들어 놨다.
  • 이게 무슨 소리냐하면 AdministratorAccess 정책은 ROOT에 준하는 권한이 정의되어 있다는 것!
  1. ROOT에는 고유 ID가 있고, 그걸 외우기 힘들어서 쓰는게 별칭

52강, Spring Boot 프로젝트, AWS CLI, 테라폼

🔎 정리

  1. AWS 대표 핵심자원 3가지
  • IAM(인증, 인가에 관련된 기능)

    • 권한, 정책
    • 역할, 사용자
  • VPC(네트워크에 관련된 기능)

  • EC2(컴퓨터에 관련된 기능)

  • AWS 자원을 다루는 방법

    • AWS 콘솔 : 일일이 하나하나 클릭해서 하는 것
    • AWS CLI : 명령어로 하는 것
      • 일괄 처리 가능
      • AWS 콘솔에 비해서 효율적
  1. AWS 자원을 다루는 방법
  • AWS 콘솔 : 일일이 하나하나 마우스로 클릭해서 하는 것
  • AWS CLI(AWS Command Line Interface) : 명령어로 하는 것
    • 일괄 처리 가능
    • AWS 콘솔에 비해서 효율적. 단, 명령어를 외워야한다.
  • 테라폼 : AWS CLI를 쉽게 사용할 수 있게 하는 것
  1. AWS API
  • AWS의 모든 처리는 내부적으로 API
  • 어떤 일을 할 때 AWS 콘솔에 접속해서 클릭을 통해 진행해도 되고, AWS API를 이용해도 된다.
  • AWS API 명령어를 통해서 쉽게 사용하라면 AWS CLI를 설치해야한다.
  • AWS CLI를 설치후 IAM USER의 엑세스키를 등록해야한다.

53강, Spring Boot 프로젝트, AWS CLI 설치 및 로그인

🔎 AWS CLI 설치

  1. AWS - AWS CLI 설치 - https://docs.aws.amazon.com/ko_kr/cli/latest/userguide/getting-started-install.html
  • 윈도우 사용자

    • 1번 링크 클릭해서 Window OS 클릭
    • 검은색 밑줄 클릭해서 설치
  • MAC 사용자

    • Command line installer - All users 로 설치

🔎 AWS CLI 설치확인

  1. 윈도우 사용자
  • 윈도우 키 누르고 cmd 누르고 엔터
  • aws 명령어 실행
  1. MAC 사용자
  • 터미널 창 띄우기
  • aws 명령어 실행
  1. 아래 사진과 같이 뜨면 성공

🔎 admin 계정의 엑세스키 발급받기

  1. AWS - IAM 사용자 - https://us-east-1.console.aws.amazon.com/iamv2/home?region=ap-northeast-2#/users
  • 1번 링크 누르고 'admin' 클릭
  • 빨간박스 (액세스 키 만들기) 클릭
  • 1, 2, 3 순서대로 클릭
  • 1번 빨간박스 입력 후 2번 빨간박스 클릭
  • 액세스 키, 비밀 액세스 키 따로 저장해놓기 (유출 되면 절대 안된다.)
    • 다시는 못보기 때문에
  • cmd 열어서 aws configure 입력
  • AWS Access Key ID [None]에 발급받은 액세스 키 입력 후 엔터
  • AWS Secret Access Key ID [None]에 발급받은 액세스 비밀 키 입력 후 엔터
  • Default region name [None]에 ap-northeast-2 입력 (서울)
  • Default output format [None]은 그냥 엔터 쳐도 된다.
  • 적용이 끝났으면 aws s3 ls 입력 후 아무것도 안뜨고 명령어창으로 돌아가면 완료
  1. IAM - admin 계정 - 보안자격증명
  • 빨간 박스에 엑세스 키가 발급되어 있으면 완료

🔎 AWS CLI 명령어

  1. aws configure list : AWS 엑세스키가 등록되어 있는지 확인
  2. aws configure
  • AWS 엑세스키 등록
    • 엑세스 키, 시크릿 엑세스키 등록
    • 재등록도 가능
  • 리전 : ap-northeast-2
    • ap-northeast-2 : AWS 서울
  • aws s3 ls
    • 아무 결과가 안나오면 성공적으로 로그인 되었다고 볼 수 있습니다.
  1. 엑세스키 삭제하는 방법
  • 콘솔 -> IAM USER -> admin 유저 선택 -> 보안자격증명 -> 액세스 키 -> 비활성화 -> 삭제
  1. WS CLI에 입력된 엑세스키 삭제하는 방법
  • 윈도우 터미널
    • rmdir /s /q "%USERPROFILE%/.aws"
  • 윈도우 파워쉘
    • rm -force -recurse $HOME.aws
  • MAC SHELL OR BASH SHELL
    • rm -rf ~/.aws

54강, Spring Boot 프로젝트, 테라폼 설치 및 프로젝트 생성

🔎 테라폼 CLI 설치

  1. 테라폼 - 설치 - https://developer.hashicorp.com/terraform/install
  • 윈도우
    • C:\lib\terraform 폴더 생성
      • amd64 다운로드
      • 안되면 386 다운로드
    • C:\lib\terraform 폴더에 terraform.exe 파일 저장
    • 환경변수의 PATH 에 C:\lib\terraform 등록

🔎 테라폼 프로젝트 폴더 생성

  1. 프로젝트 경로 : ~/IdeaProjects/terraform-project-1
  • ~ : Home 경로
  • IdeaProject : 인텔리제이 프로젝트들 있는 폴더

🔎 .gitignore 폴더 생성 후 .idea, .terraform 추가

  • 프로젝트 우클릭 -> New -> File -> 파일명 .gitignore 입력해서 생성
  • 아래 사진과 같이 입력

🔎 main.tf

  • 프로젝트 우클릭 -> New -> File -> 파일명 main.tf 입력해서 생성하면 terraform 파일이 생성된다.
  • 생긴 파일에 아래 코드 입력
terraform {
  // 자바의 import 와 비슷함
  // aws 라이브러리 불러옴
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }
  }
}

# Configure the AWS Provider
provider "aws" {
  region = "ap-northeast-2"
}

resource "aws_vpc" "example" {
  cidr_block = "10.0.0.0/16"

  tags = {
    Name = "example"
  }
}
  • 우측 상단에 Install Terraform and HCL Plugin 클릭해서 설치

🔎 테라폼 명령어

  1. terraform init
  • 라이브러리 다운로드
  • 라이브러리 관련 소스코드가 바뀔 때 마다 실행해야 한다.
  1. terraform plan
  • 실제 리소스 생성을 하는것은 아니고, 현재 소스코드가 실행가능한지 검사
  1. terraform apply
  • 리소스 생성
  • yes 입력
    • 아래 사진과 같이 AWS 콘솔 -> VPS에 추가 됐으면 성공
  1. terraform destroy
  • 리소스 삭제
  • yes 입력
    • 아래 사진과 같이 AWS 콘솔 -> VPS에 지운 VPS만큼 사라져 있으면 성공
  1. ✔ 주의사항
  • AWS 콘솔 볼때 아래 사진과 같이 지역이 서울인지 확인해야한다.
    • 이유 : 내가 region을 서울로 했기 때문에

55강, Spring Boot 프로젝트, 테라폼으로 EC2 1개 생성


56강, Spring Boot 프로젝트, docker login ghcr.io

🔎 간략한 정리

  1. 나중에 EC2에서 private docker image를 받기 위해 로그인
  2. read 패키지 권한만 있는 GITHUB 토큰 생성
  3. 도커 이미지에는 application-secret.yml 이 포함
  4. EC2에서 도커 로그인(ghcr.io)

1. Session Manager 연결하는 법

  • 인스턴스 ID 클릭

  • 우측에 연결 클릭

  • Session Manager 선택 후 연결 버튼 클릭

  • 아래와 같은 창이 열리면 성공

2. read 패키지만 있는 Github 토큰 발급 받는 법

  • Github 프로필 눌러서 Settings 클릭

  • 왼쪽 메뉴 맨 아래에 Developer settings 클릭

  • 왼쪽 메뉴에서 Personal access tokens -> Tokens (classic) 클릭

  • 가운데 Generate new token 클릭 -> Generate new token (classic) 클릭

  • 빨간박스 1, 2순으로 한 후에 Generate token 버튼 클릭

  • 발급 완료, 토큰은 다시 볼 수 없으므로 따로 저장해놓자.

3. AWS EC2 인스턴스 Session Manager에서 ghcr.io 로그인

  • AWS EC2 인스턴스 Session Manager에 연결한 후 sudo su 입력하면 아래 사진 처럼 입력창이 나타나는데 거기다 아래 코드를 입력하면 도커가 로그인 된다.
    • YOUR_TOKEN : 아까 깃허브에서 발급받은 토큰을 넣어주면 된다.
docker login ghcr.io -u USERNAME -p YOUR_TOKEN
# ex) docker login ghcr.io -u jhs512 -p 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

  • 아래와 같이 뜨면 로그인 성공

57강, Spring Boot 프로젝트, MySQL 설치, 세팅

🔎 간략한 정리

  1. MySql을 도커로 실행
  2. dbeaver로 연결 생성
  3. root계정의 근거리 비번 세팅
  4. root계정의 원거리 비번 세팅

1. AWS EC2 인스턴스 Session Manager에서 MySQL 설치

  • 아래 코드를 입력후 Enter
docker run -d \
  --name mysql_1 \
  --restart unless-stopped \
  -p 3306:3306 \
  -e TZ=Asia/Seoul \
  -e MYSQL_ROOT_PASSWORD=koreait123414 \
  -v /dockerProjects/mysql_1/volumns/var/lib/mysql:/var/lib/mysql \
  mysql \
  --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
  • 아래와 같이 뜨면 성공

2. docker ps -a 명령어 입력해서 도커 돌아가고 있는지 확인하기

  • 아래와 같이 뜨면 성공

3. MySQL 에 새 비밀번호 세팅, 로컬용 비밀번호 세팅

docker exec -it mysql_1 mysql -u root -p #  Docker 컨테이너 내부에서 MySQL 클라이언트를 실행하여 MySQL에 접속하겠다.
# 비번입력 : koreait123414
ALTER USER 'root'@'%' IDENTIFIED BY '새로운 비밀번호'; # 새 비번 입력
ALTER USER 'root'@'localhost' IDENTIFIED BY '1234'; # 로컬에서는 1234 로 접속 할 수 있도록
FLUSH PRIVILEGES; # MySQL에서 사용자 권한 테이블을 새로 고침하는 역할
exit # Mysql 명령창 나가기

58강, Spring Boot 프로젝트 Redis 설치, 세팅

🔎 간략한 정리

  1. 레디스를 도커로 설치
  2. 레디스를 도입하면 스프링부트가 교체되면서 발생하는 로그인 풀림 현상을 방지할 수 있다.
  3. 레디스에 비밀번호 설정

1. AWS EC2 인스턴스 Session Manager에서 Redis 설치

  • 아래 코드를 입력하고 Enter
docker run -d \
  --name=redis_1 \
  --restart unless-stopped \
  -p 6379:6379 \
  -e TZ=Asia/Seoul \
  redis
  • 아래 사진과 같이 뜨면 성공

2. Redis 비밀번호 변경

  • 기본적으로 Redis는 비밀번호가 설정되어있지 않다.
  • 아래 코드 참고
docker exec -it redis_1 redis-cli (Redis에 접속)
AUTH 기존_비밀번호 # 기존에 설정된 비밀번호가 있다면 입력 (Redis 로그인 하는 법)
CONFIG SET requirepass 새로운_비밀번호 # 새 비번 입력
EXIT

59강, Spring Boot 프로젝트,

1. AWS EC2 인스턴스 Session Manager에서 Nginx Proxy Manager 설치

docker run -d \
  --name npm_1 \
  --restart unless-stopped \
  -p 80:80 \
  -p 443:443 \
  -p 81:81 \ # 81번 포트 사용
  -e TZ=Asia/Seoul \
  -v /dockerProjects/npm_1/volumes/data:/data \
  -v /dockerProjects/npm_1/volumes/etc/letsencrypt:/etc/letsencrypt \
  jc21/nginx-proxy-manager:latest
  • 아래 사진과 같이 뜨면 성공

2. nginx proxy manager 관리 콘솔에 접속

  • Full Name / Nickname 바꾸는건 선택사항, Email은 본인 이메일 입력 후 save 버튼 클릭

  • Current Password에 changeme 입력 후 본인이 사용할 비밀번호와 비밀번호 확인 입력 후 save 버튼 클릭

  • DNSZI 로그인 하고 등록해놓은 도메인 선택 후 호스트IP 관리(A레코드) 선택

  • A레코드 란에 api.www.surl 입력 후 IP코드에 13.124.163.193 입력

    • 13.124.163.193 : AWS EC2의 해당 인스턴스의 IP주소
  • 가운데 Add Proxy Host 버튼 클릭

  • 상단 Details에서 Domain Names에 api.www.surl.ygcqwe.site 입력

  • 나머지는 아래 사진과 같이 입력하고 선택한 후 save 버튼 클릭

  • 아래와 같이 나타나면 등록 완료

  • 우측 3점 클릭 -> Edit 클릭 후 아래 사진과 같이 해준 후 save 버튼 클릭

3. 접속확인

  • 빨간박스(등록한 Proxy Host)클릭

  • 아래 사진과 같이 뜨면 성공

    • 502가 뜨는 이유는 정작 저기에서 돌아가는 스프링부트가 없기 때문이다.
  • 빨간 원 눌러서 2번 빨간박스 같이 뜨는게 정상이다.

4.

  • mkdir -p /dockerProjects/surl/source : -p를 붙이면 연쇄적으로 만든다.

60강, Spring Boot 프로젝트, 수동배포 시도, 실패

🔎 간략한 정리

  1. 오늘 한건 수동 배포
  • EC2 인스턴스에서 git clone
  • application-secret.yml 생성 -> vim 명령어로
  • 도커 이미지 생성
  • 도커 컨테이너 생성
  • 도커 컨테이너에서 뭐가 돌아가는지 확인 : docker ps 명령어로
  • 도커 컨테이너 출력문(로그) 확인 : docker logs -f 도커컨테이너이름
    • ex) docker logs -f surl_1

1. 도커 이미지 빌드 준비를 위한 소스코드 다운로드

mkdir -p /dockerProjects/surl/source
cd /dockerProjects/surl/source
git clone https://github.com/Yu-Geun-Chan/Surl_project_11.git

2. src/main/resources/application-secret.yml 파일 생성

# vim 열기
vim src/main/resources/application-secret.yml
# 키보드 'a' 키 입력
# 프로젝트 application-secret.yml에 있는거 복사해서 입력
# 키보드 'ESC' 키 입력
# 키보드 ':wq!' 키 입력
# 키보드 'ENTER' 키 입력

3. 도커 이미지 생성

  • cd Surl_project_11 입력해서 Surl_project_11 프로젝트 내부로 들어가고 아래 코드 입력
docker build -t geunchan9055/surl:latest .

4. 도커 컨테이너 실행

  • 8080 : 해당하는 port 번호 입력
  • geunchan9055/surl:latest : 도커 빌드할때 입력했던 것
docker run -d \
  --name=surl_1 \
  --restart unless-stopped \
  -p 8081:8080 \
  -e TZ=Asia/Seoul \
  -v /dockerProjects/surl/volumes/gen:/gen \
  geunchan9055/surl:latest

5. 도커 컨테이너 출력문(로그) 확인

docker logs -f surl_1

6. 도커 컨테이너에서 뭐가 돌아가는지 확인

docker ps

61강, Spring Boot 프로젝트, 수동배포 시도, 성공

🔎 간략한 정리

  1. 운영 DBMS(EC2)에서 surl_prod DB 생성
  2. application-prod.yml 에서 운영 DB 정보 수정
  3. git push
  4. EC2 인스턴스에서 기존 컨테이너 삭제
  5. 기존 이미지 삭제
  6. git pull
  7. 이미지 생성
  8. 컨테이너 생성
  9. MySql에서 root@172.17.0.1 계정 생성, 권한 부여
  10. api.www.surl.ygcqwe.site로 운영서버에 접속
  11. DB table 생성 확인
  12. fly.io, AWS
  • fly

    • 소스코드 : fly deploy 도커 파일로 이미지 생성해서 앱 등록

      • 머신 2개
    • DB : DB 앱 생성

      • 머신 3개
    • -> 무중단 CI/CD + 배포 를 위해서 github Action

    • -> push 했을 때 알아서 재배포 하도록 -> 자동배포

  • AWS

    • IAM 계정 생성 -> 정책
    • 테라폼
    • github 토큰
    • VPC 자원 생성
    • EC2 생성
    • MYSQL, Redis, NPM
    • git clone -> 이미지 -> 컨테이너 -> 실행 -> 배포

1. EC2 인스턴스 재부팅

reboot now
  • 2, 3분 후 해당 브라우저를 리프레시 후 sudo su 다시 입력하면
  • 다시 쉘에 접근할 수 있다.

2. 도커 이미지 빌드 준비를 위한 소스코드 다운로드

cd /dockerProjects/surl/source
git pull origin main
기존 컨테이너, 이미지 삭제
docker rm -f surl_1
docker rmi surl

3. surl 서비스에서 접속할 때 사용할 root@172.17.0.1 MySQL 계정 생성

docker exec -it mysql_1 mysql -u root -p

비번입력 : koreait123414

CREATE USER 'root'@'172.17.0.1' IDENTIFIED BY '1234'; -> 사용자한테 많은 권한을 준다.

GRANT ALL PRIVILEGES ON *.* TO 'root'@'172.17.0.1'; -> 권한을 주겠다.

FLUSH PRIVILEGES; -> 새로고침

# REVOKE ALL PRIVILEGES ON _._ FROM 'root'@'172.17.0.1'; # 이 명령어는 알아두세요. root@172.17.0.1 사용자의 권한을 삭제하는 명령어 입니다.

exit

4. 컨테이너 실행

docker run -d \
 --name=surl_1 \
 --restart unless-stopped \
 -p 8081:8080 \
 -e TZ=Asia/Seoul \
 -v /dockerProjects/surl/volumes/gen:/gen \
 axdsw121/surl:latest

62강, Spring Boot 프로젝트, 자동배포

🔎 간략한 정리

  • socat 설치
  • 파이썬 설치
  • 기존 컨테이너 제거
  • 기존 이미지 제거
  • 코드 변경사항 적용 (branch 주의 - main, master)
  • github 시크릿 변수들 설정
  • aws access key가 일치해야함
  • 커밋, 푸시 -> 릴리즈, 패키지 생성
  • 릴리즈 생성시에 만들어지는 버전을 사용하기 위해서
  • 릴리즈 버전 -> 도커 이미지 버전
  • 패키지 세팅 -> public을 private -> 도커 이미지를 private 화 하도록
  • 무중단 배포 되는지 확인

1. application-secret.yml

custom:
  secret:
    spring:
      data:
        redis:
          password: koreait123414
    key: 1234

2. socat 설치

yum install socat -y

3. python 설치

yum install python -y

4. Surl_project_11에 infraScript/zero_downtime_deploy.py 파일추가

#!/usr/bin/env python3

import os
import requests  # HTTP 요청을 위한 모듈 추가
import subprocess
import time
from typing import Dict, Optional


class ServiceManager:
    # 초기화 함수
    def __init__(self, socat_port: int = 8081, sleep_duration: int = 3) -> None:
        self.socat_port: int = socat_port
        self.sleep_duration: int = sleep_duration
        self.services: Dict[str, int] = {
            '${도커이미지명}_1': 8082,
            '${도커이미지명}_2': 8083
        }
        self.current_name: Optional[str] = None
        self.current_port: Optional[int] = None
        self.next_name: Optional[str] = None
        self.next_port: Optional[int] = None

    # 현재 실행 중인 서비스를 찾는 함수
    def _find_current_service(self) -> None:
        cmd: str = f"ps aux | grep 'socat -t0 TCP-LISTEN:{self.socat_port}' | grep -v grep | awk '{{print $NF}}'"
        current_service: str = subprocess.getoutput(cmd)
        if not current_service:
            self.current_name, self.current_port = '${도커이미지명}_2', self.services['${도커이미지명}_2']
        else:
            self.current_port = int(current_service.split(':')[-1])
            self.current_name = next((name for name, port in self.services.items() if port == self.current_port), None)

    # 다음에 실행할 서비스를 찾는 함수
    def _find_next_service(self) -> None:
        self.next_name, self.next_port = next(
            ((name, port) for name, port in self.services.items() if name != self.current_name),
            (None, None)
        )

    # Docker 컨테이너를 제거하는 함수
    def _remove_container(self, name: str) -> None:
        os.system(f"docker stop {name} 2> /dev/null")
        os.system(f"docker rm -f {name} 2> /dev/null")

    # Docker 컨테이너를 실행하는 함수
    def _run_container(self, name: str, port: int) -> None:
        os.system(
            f"docker run -d --name={name} --restart unless-stopped -p {port}:${스프링부트-운영모드-포트} -e TZ=Asia/Seoul -v /dockerProjects/${도커이미지명}/volumes/gen:/gen --pull always ghcr.io/${깃허브-아이디}/${도커이미지명}")

    def _switch_port(self) -> None:
        # Socat 포트를 전환하는 함수
        cmd: str = f"ps aux | grep 'socat -t0 TCP-LISTEN:{self.socat_port}' | grep -v grep | awk '{{print $2}}'"
        pid: str = subprocess.getoutput(cmd)

        if pid:
            os.system(f"kill -9 {pid} 2>/dev/null")

        time.sleep(5)

        os.system(
            f"nohup socat -t0 TCP-LISTEN:{self.socat_port},fork,reuseaddr TCP:localhost:{self.next_port} &>/dev/null &")

        # 서비스 상태를 확인하는 함수

    def _is_service_up(self, port: int) -> bool:
        url = f"http://127.0.0.1:{port}/actuator/health"
        try:
            response = requests.get(url, timeout=5)  # 5초 이내 응답 없으면 예외 발생
            if response.status_code == 200 and response.json().get('status') == 'UP':
                return True
        except requests.RequestException:
            pass
        return False

    # 서비스를 업데이트하는 함수
    def update_service(self) -> None:
        self._find_current_service()
        self._find_next_service()

        self._remove_container(self.next_name)
        self._run_container(self.next_name, self.next_port)

        # 새 서비스가 'UP' 상태가 될 때까지 기다림
        while not self._is_service_up(self.next_port):
            print(f"Waiting for {self.next_name} to be 'UP'...")
            time.sleep(self.sleep_duration)

        self._switch_port()

        if self.current_name is not None:
            self._remove_container(self.current_name)

        print("Switched service successfully!")


if __name__ == "__main__":
    manager = ServiceManager()
    manager.update_service()

5 .github/workflows/deploy.yml 수정

  • ${도커이미지명} : 내가 생성한 도커 이미지 명
    • ex) surl
  • ${AWS-EC2-인스턴스-ID} : 내 인스턴스 ID, 아래 사진 참고
  • 해당 리포지터리 우측에 아래와 같이 됐다면 잘 된 것
name: 'deploy'
on:
  push:
    paths:
      - '.github/workflows/**'
      - 'src/**'
      - 'build.gradle'
      - 'Dockerfile'
      - 'readme.md'
      - 'infraScript/**'
    branches:
      - 'main'
jobs:
  makeTagAndRelease:
    runs-on: ubuntu-latest
    outputs:
      tag_name: ${{ steps.create_tag.outputs.new_tag }}
    steps:
      - uses: actions/checkout@v4
      - name: Create Tag
        id: create_tag
        uses: mathieudutour/github-tag-action@v6.1
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
      - name: Create Release
        id: create_release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ steps.create_tag.outputs.new_tag }}
          release_name: Release ${{ steps.create_tag.outputs.new_tag }}
          body: ${{ steps.create_tag.outputs.changelog }}
          draft: false
          prerelease: false
  buildImageAndPush:
    name: 도커 이미지 빌드와 푸시
    needs: makeTagAndRelease
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Docker Buildx 설치
        uses: docker/setup-buildx-action@v2
      - name: 레지스트리 로그인
        uses: docker/login-action@v2
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - name: set lower case owner name
        run: |
          echo "OWNER_LC=${OWNER,,}" >> ${GITHUB_ENV}
        env:
          OWNER: "${{ github.repository_owner }}"
      - name: application-secret.yml 생성
        env:
          ACTIONS_STEP_DEBUG: true
          APPLICATION_SECRET: ${{ secrets.APPLICATION_SECRET_YML }}
        run: echo "$APPLICATION_SECRET" > src/main/resources/application-secret.yml
      - name: 빌드 앤 푸시
        uses: docker/build-push-action@v3
        with:
          context: .
          push: true
          tags: |
            ghcr.io/${{ env.OWNER_LC }}/${도커이미지명}:${{ needs.makeTagAndRelease.outputs.tag_name }},
            ghcr.io/${{ env.OWNER_LC }}/${도커이미지명}:latest
  deploy:
    runs-on: ubuntu-latest
    needs: [ buildImageAndPush ]
    steps:
      - name: AWS SSM Send-Command
        uses: peterkimzz/aws-ssm-send-command@master
        id: ssm
        with:
          aws-region: ${{ secrets.AWS_REGION }}
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          instance-ids: "${AWS-EC2-인스턴스-ID}"
          working-directory: /
          comment: Deploy
          command: |
            mkdir -p /dockerProjects/${도커이미지명}
            curl -o /dockerProjects/${도커이미지명}/zero_downtime_deploy.py https://raw.githubusercontent.com/${깃허브-아이디}/${깃허브-리포지터리명}/main/infraScript/zero_downtime_deploy.py
            chmod +x /dockerProjects/${도커이미지명}/zero_downtime_deploy.py
            /dockerProjects/${도커이미지명}/zero_downtime_deploy.py

🔎 액세스 키, 비밀 액세스 키, 리전 일치하는지 확인하는 법

  • git bash에서 aws configure list를 입력했을 때 첫번째 사진의 빨간박스 3개가 settings -> Secrets and vairalbes -> Actions에 등록했던 것들과 일치하면 잘 설정된 것!
  • 일치하지 않는다면 해당 링크 참고해서 진행하기를..

7. 도커 이미지 private 설정

  • 해당 리포지터리 Packages의 등록된 패키지 클릭
  • 우측 하단 Package settings 클릭
  • 맨 아래의 Change visibility 클릭
  • Private 선택 후 패키지 이름(surl) 입력하고 아래 버튼 클릭

63강, Spring Boot 프로젝트, 회원가입 API 구현


64강, Spring Boot 프로젝트, Ut class


65강, Spring Boot 프로젝트, 예외처리는 return 보다 throw

🔎 간략한 정리

  • 예외상황에서 return 보다 throw가 좋은 이유
  • return 으로 하면 리턴타입이 허용하는 범위 내에서만 표현 가능
  • 표현이 어색해질 수 있다.
  • throw는 리턴타입과 상관 X
  • 다만 json 메서드 return 이라면 응답결과는 깔끔

66강, Spring Boot 프로젝트, ExceptionHandler를 이용한 예외처리

🔎 간략한 정리

  • return은 해당 statement가 포함된 함수만 끝내지만
  • throw는 예외처리를 호출자에서 해주지 않으면 해당 쓰레드 전체가 끝난다
    • 즉 해당 HTTP 요청에 대한 처리 자체가 끝난다
  • 예외를 발생시키고 예외처리를 하지 않으면 스프링부트가 메뉴얼대로 오류를 처리한다
  • @ControllerAdvice 클래스에 @ExceptionHandler 메서드를 만들면 오류에 대한 처리를 가로챈다
  • @ExceptionHandler 메서드가 여러개라면 해당 예외상황에 더 적절한 메서드가 알아서 선택됨
  • @ExceptionHandler 메서드가 존재해도 예외상황에 맞지 않다면 메서드는 작동하지 않음

67강, Spring Boot 프로젝트, 백엔드의 사용자는 사람 or 기계

🔎 간략한 정리

  • 기계는 '정보'만 필요하다.
  • 사람은 '정보'와 '꾸밈'이 필요하다.
  • 스프링부트 앱에서는 '정보'와 '꾸밈'을 모두 처리할 수 있다.
    • ex) JSP, Thymeleaf
  • 스프링부트 앱에서 '정보'만 처리하는 경우
    • 이때는 따로 프론트엔드 서버를 만든다.
      • ex) 리액트, 스벨트, 앵귤러, 뷰

====================================================================================

  • 기계가 원하는 것(정보) : {"age" : 22, "name" : "철수"}
  • 사람이 원하는 것(정보 + 꾸밈) : 1명의 사람이 있는데 그 사람의 이름은 철수고, 나이는 22살입니다.

====================================================================================

  • 스프링부트만 만든다(MPA 방식)
    • 정보 + 꾸밈을 스프링부트에 동시에(기존에 해왔던 방식)
  • 스프링부트 + 프론트엔드(SPA 방식)
    • 스프링부트 : 정보를 담당
    • 리액트 or 스벨트 : 꾸밈을 담당
    • 따로 프론트엔드 서버가 있을 경우에는 스프링부트로 REST API 서버를 구현한다.

106일차

68강, SpringBoot 프로젝트, POSTMAN, HTTP 메서드

🔎 간략한 정리

  1. 스프링 부트, 브라우저, 프론트엔드 앱
  • SpringBoot <-> user(브라우저)
    • 요청 : GET PATH?
    • 응답 : 정보 + 꾸밈
  • SpringBoot <-> F_App(프론트엔드 앱)
    • 요청 : GET, POST, PUT or PATCH, DELETE ...
    • 응답 : JSON, XML
    • test : Postman으로
  1. POSTMAN
  • 기계(프론트엔드 앱(F_App))와 통신하는 스프링부트 앱을 만드는 행위를 'REST API 서버 구현' or 'API 서버 구현이라 한다.
  • API 서버를 구현하면서 테스트를 할 때는 브라우저만으로 충분하지 않다.
    • 브라우저의 주소표시줄로는 GET 요청만 발생시킬 수 있기 때문에
  • 이러할때 POSTMAN을 쓴다.
    • 해당 툴로 GET, POST, PUT, PATCH, DELETE HTTP 메서드 요청을 할 수 있으므로
    • 쿼리스트링과 요청 본문도 요청에 담고 결과도 확인이 가능하므로
  1. HTTP 메서드
  • GET : 조회
  • POST : 생성
  • PUT or PATCH : 수정
  • DELETE : 삭제
  • URL 은 동사형보다는 명사형으로 쓰는게 좋다.
  1. 게시물 CRUD 로 알아보는 올바른 HTTP 요청 방법
  • 게시물 생성
    • 잘못된 방법 : GET /article/write?title=제목&body=내용
    • 올바른 방법 : POST /articles
      • 헤더, Content-Type : application/json
      • body : {"title": "제목", "body": "내용"}
  • 게시물 1번 수정
    • 잘못된 방법 : GET /article/modify?id=1&title=제목 new&body=내용 new
    • 올바른 방법 : PUT /articles/1
      • 헤더, Content-Type : application/json
      • body : {"title": "제목 new", "body": "내용 new"}
  • 게시물 1번 조회
    • 잘못된 방법 : GET /article/detail?id=1
    • 올바른 방법 : GET /articles/1
  • 게시물들 조회
    • 잘못된 방법 : GET /article/list
    • 올바른 방법 : GET /articles
  • 게시물 1번 삭제
    • 잘못된 방법 : GET /article/delete?id=1
    • 올바른 방법 : DELETE /articles/1

69강, SpringBoot 프로젝트, ApiV1MemberController 생성


70강, SpringBoot 프로젝트, 예외발생시 상태코드를 명시

🔎 간략한 정리

  1. HTTP 응답코드
  • 2xx : 성공
  • 4xx : 실패 -> 클라이언트 때문에
  • 5xx : 실패 -> 서버 때문에
  1. @ResponseStatus(HttpStatus.BAD_REQUEST) : 400으로 고정
  2. ResponseEntity : 상황에 따라서 다른 상태코드 지정 가능

71강, SpringBoot 프로젝트, 성공과 실패의 양식을 통일


72강, SpringBoot 프로젝트, Validation(유효성 체크 의존성)


73강, SpringBoot 프로젝트, 기타 사항 예외처리

🔎 간략한 정리

  1. 2가지
  • Exception
    • GlobalException 발생
    • MethodArgumentNotValidException 발생
  1. MethodArgumentNotValidException 처리
  • MethodArgumentNotValidException 핸들러 추가
  • 해당 핸들러에서 GlobalException 형태로 변형
  1. 응답의 양식이 같음
  • 양식 : resultCode, statusCode, msg, data
  • 케이스
    • 성공할 경우
    • 실패할 경우 1 : GlobalException 발생
    • 실패할 경우 2 : MethodArgumentNotValidException
  • 기타 예외 핸들러
    • trace : 예외에 대한 자세한 발생경위
    • path : URL
  • rq
    • rq는 요청과 응답 쌍에 대한 추상화된 레이어
    • rq는 내부적으로 요청(HttpServletRequest)과 응답(HttpServletResponse)에 접근하고 다룰 수 있다
  • 기타 예외에 대한 처리를 하지 않으면 기본 양식과 동일한 양식으로 오류가 출력된다

74강, SpringBoot 프로젝트, 액션의 응답본문 클래스

🔎 간략한 정리

  1. 엔드포인트 == 액션메서드
  2. 액션 메서드마다 보통 클래스 2개씩 필요하다
  • 요청본문 클래스
    • 입력값이 없다면 필요없음
  • 응답본문 클래스
  1. MemberJoinRespBody 클래스
  • join 메서드가 RsData <MemberJoinRespBody> 타입 데이터를 리턴하도록

75강, SpringBoot 프로젝트, Aspect, RsData 상태코드를 응답코드로

🔎 간략한 정리

  1. 성공했을 때 응답코드
  • 현재는 무조건 200 (또는 설정되어 있는거)
  1. Aspect
  • Aspect가 적은 코딩량으로 특정 패턴의 코드가 실행되기 전/후에 추가작업이 가능해진다
  • RsData 상태코드를 응답코드로

76강, SpringBoot 프로젝트, REST API, Surl 등록, 단건조회


77강, SpringBoot 프로젝트, DTO

🔎 간략한 정리

  • 엔티티는 직접적으로 노출되면 안됨
    • @JsonIgnore가 있지만 임시방편
    • 엔티티의 칼럼명을 바꾸지 못함
    • 프론트엔드 앱에서 오류가 발생
  • DTO가 필요하다
    • 엔티티는 API를 통해서 노출될 때 무조건 DTO로 변경 되어야 함
    • DTO는 엔티티를 포함하면 안됨
    • DTO 객체는 엔티티 객체로부터 만드는게 일반적이다
    • DTO는 엔티티와 구조가 거의 비슷함
    • Member author 는 분리해서 적절한 필드로 변경
    • long authorId
    • String authorName

78강, SpringBoot 프로젝트, OSIV 끄기

🔎 간략한 정리

  1. DBCP
  • 스프링부트에서 DB와의 통신 회선(DB 커넥션)을 미리 여러개 만들어 둔다
  • 요청이 몰릴 때를 대비하여 미리 해놓는 것
  1. OSIV
  • 기본적으로 켜져있음
  • 이로 인해 하나의 요청이 시작될 때 DBCP에서 DB 커넥션 하나가 배정된다
  • 응답이 완료되면 그 때 DB 커넥션이 회수된다
  • 이 방식은 비효율적이다. 대신 코딩이 편하다
  1. REST API 에서는 OSIV 끄기
  • 스프링부트로 타임리프나 JSP로 MPA를 만드려는게 아니고 REST API를 구현하는게 목표라면 보통 끈다
  • 대신 컨트롤러의 액션메서드마다 @Transactional을 붙여야 함

79강, SpringBoot 프로젝트, REST API, surl 삭제


80강, SpringBoot 프로젝트, REST API, SURL 다건조회


81강, SpringBoot 프로젝트, REST API, SURL 수정

🔎 간략한 정리

  1. 더티체킹에 의해 기존 내용과 수정할 내용이 같으면 select만 하고 update는 진행하지 않는다.

82강, SpringBoot 프로젝트, 권한체크

🔎 간략한 정리

  1. 소유권 체크
  • 조회, 수정, 삭제 전에 행위자(로그인 한 사람)가 surl 객체의 소유주인지 체크
  1. rq.getMember()는 현재 1번 회원으로 리턴되는 중

83강, SpringBoot 프로젝트, AuthService, 권한체크

🔎 간략한 정리

  1. Author에 대한 권한체크를 따로 분리시켜줬다.
  • AuthService 클래스 생성

84강, SpringBoot 프로젝트, 매 요청마다 파라미터 정보로 인증

🔎 간략한 정리

  • 서버는 사용자를 구분하지 못한다.
    • 서버에게 내 정보 줘 라고 하면? 너 누군데? 라고 한다.
    • 서버에게 내 정보 줘, 나는 user1이야 하면? 서버는 user1에 대한 정보를 준다.
    • 매 요청마다 본인이 누군지에 대한 정보(user1)를 포함시켜야 한다.
    • user1과 같은 정보를 인증정보라고 한다.
  • surl 다건조회할 때 인증정보를 포함시켜보자.
    • 해당 액션메서드에서 인증정보를 기준으로 현재 로그인 한 사람이 누구인지 파악하도록

85강, SpringBoot 프로젝트, Rq에서 인증처리

🔎 간략한 정리

  • rq.getMember(); -> null 리턴
    • rq.setMember(...); 를 사전에 했었어야함
    • 매 액션메서드마다 해당작업을 해줘야함
    • 더 좋은 방법을 찾자
  • rq.getMember 메서드만으로 인증처리
    • Rq는 리퀘스트 스코프 빈
    • HttpServletRequest 객체에 접근이 가능함
    • 따라서 파라미터로 넘겨진 actorUsername에 접근이 가능

86강, SpringBoot 프로젝트, getMember에 캐시 로직 추가


87강, SpringBoot 프로젝트, Authorization Header


88강, SpringBoot 프로젝트, 쿠키

🔎 간략한 정리

  1. 쿠키
  • 변수(저장공간)라고 생각하면 편하다.
  • http 클라이언트와 http 서버가 공유하는 저장공간
  • http 클라이언트에 저장한다.
  • 쿠키는 특정 사이트에 귀속되고, 해당 사이트로의 통신(요청)이 발생하면 관련된 쿠기가 자동으로 헤더에 압축되어 포함된다.

89강, SpringBoot 프로젝트, 쿠키로 로그인, 로그아웃


90강, SpringBoot 프로젝트, 스프링 시큐리티 의존성 추가


91강, SpringBoot 프로젝트, 기본설정에서 특정 URL만 허용


92강, SpringBoot 프로젝트, csrf 끄기


93강, SpringBoot 프로젝트, 비밀번호 암호화


94강, SpringBoot 프로젝트, 비밀번호 인증에 인코더 적용

🔎 간략한 정리

  • passwordEncoder로 암호화 한 것은 passwordEncoder로 검증해야 함

95강, SpringBoot 프로젝트, CustomAuthenticationFilter 추가

🔎 간략한 정리

  • 시큐리티의 입구컷

    • 단건조회 API 호출하면 입구컷 된다.
    • 로그인을 제대로 했는데 입구컷 된다.
    • 시큐리티는 나의 로그인 방식을 이해할 수 없다.
  • CustomAuthenticationFilter 추가

    • 스프링 시큐리티는 필터로 구현되어 있다.
    • 내가 원하는 필터를 적절한 시점에 실행할 수 있다.
    • 시큐리티가 나를 입구컷하기 전에 실행되어야 한다.
    • CustomAuthenticationFilter는 쿠키(actorUsername,actorPassword)가 올바른지 체크
    • 올바르다면 스프링 시큐리티가 이해할 수 있는 방식으로 로그인 되었다고 설정해야한다.

96강, SpringBoot 프로젝트, CustomAuthenticationFilter 구현

🔎 간략한 정리

  • 필터기능 구현
    • 인증정보가 없거나 올바지 않으면 예외 발생 X
    • 시큐리티 인증정보를 등록하지 않는 걸로
      • 왜냐하면 어차피 인증정보만 등록하지 않아도 사용자가 원하는 목적지로 못 간다.
    • 쿠키의 인증정보가 올바르다면 시큐리티에 인증정보를 등록
    • 이른 filterChain.doFilter 후에는 return;을 해줘야한다.

97강, SpringBoot 프로젝트, 자격없는 접근에 대한 오류

🔎 간략한 정리

  • exceptionHandling 구현
    • AppConfig 에서 static 메서드로 Jackson Bena 받아오는 기능 구현

98강, SpringBoot 프로젝트, 인증정보 더블 체크 제거

🔎 간략한 정리

  • 인증정보 더블체크 이슈(Rq, CustomAuthenticationFilter)
    • 스프링 시큐리티 인증객체 안의 name은 믿어도 된다.
    • Rq의 getMember는 위의 정보를 신뢰하므로 갖다 쓰기만 하면 된다.

99강, SpringBoot 프로젝트, username 대신 id 활용

🔎 간략한 정리

  • 회원의 식별자 변경
    • username -> id
      • 이유 : 더 빨라서, JPA에서 이 id를 기준으로 캐싱데이터 사용 여부를 결정
  • 영속성 컨텍스트의 작동으로 인한 추가 회원 조회 쿼리가 발생하지 않을 수도 있다.

100강, SpringBoot 프로젝트, apiKey 도입

🔎 간략한 정리

  • UUID : 길고, 난수성이 뛰어나서 해커가 특정회원의 apiKey(UUID)를 추측해서 알아낼 수 없다.
    • 회원마다 고유한 apiKey를 가지도록
    • username, password를 통한 인증만이 아닌 apiKey를 통한 인증도 가능해진다.
    • 필터에서 인증할 때 쿠키에서 apiKey를 사용하도록
    • 로그인 시에 올바른 username/password가 입력되었다면 쿠키로 apiKey 등록
  • 로그인 API 엔드포인트에 대해서는 permit
  • 로그인시에 matchers 활용하도록
  • apiKey 방식과 username/password 방식은 둘 다 탈취당하면 치명적이지만 apiKey가 상황상 더 낫다.

101강, Spring Boot 프로젝트(fly, AWS 배포), JWT 방식으로 API Key 생성

🔎 간략한 정리

  • 시크릿키로 서명해야 JWT가 완성된다. 이후 같은 키로 해당 JWT의 조작여부를 빠르게 알 수 있다
  • appilcation-secret.yml이 수정됐다 -> GITHUB의 시크릿 변수도 수정해야함
  • AuthTokenService
    • 특정 회원의 JWT를 생성해줌
    • JWT는 만료의 개념이 있다
    • JWT는 정보(내용)를 직접 넣을 수 있다
      • 보통 회원의 식별자를 넣는다
      • JWT를 보고 누구의 토큰인지 알아야하기 때문에
  • 로그인 성공시 accessToken 이라는 쿠키가 생기고 회원의 JWT를 저장함

102강, Spring Boot 프로젝트(fly, AWS 배포), JWT에 수명이 있는 이유

🔎 간략한 정리

  1. API KEY 종류별 비교
  • 키 자체에 정보를 담고 있는가?
    • UUID : 없음
    • JWT : 있음
  • 해당 키가 어떠한 회원의 키인지 파악하는 방법
    • UUID : SELECT * FROM member WHERE apiKey = ? 를 통해서 나온 회원
    • JWT : JWT 자체에 정보가 들어있다.
  • 생성할 때 시크릿 키가 필요한가?
    • UUID : 필요없음
    • JWT : 필요함
      • 절대로 외부에 노출되면 안됨
  • 해당 키가 유효한 키인지 확인하는 방법
    • UUID : SELECT * FROM member WHERE apiKey = ? 를 해서 결과나 나오면 유효한 키
    • JWT : 해당 JWT 를 생헝할 때 사용한 시크릿 키를 통해서 아주 빠르게 유효성 체크를 할 수 있다.
  • 중앙관리를 하는가?
    • UUID : 키 자체를 DB에서 관리한다. 언제든지 폐기, 중지, 재생성이 가능하다. 해커가 해당 키를 탈취했다는 사실을 안다면 빠르게 대처가 가능
    • JWT : 중앙관리를 하지 않는다. 해커가 해당 키를 탈취했다는 사실을 안다고 하더라도 대처가 불가능하다. 그래서 수명을 보통 수명을 5분이내로 짧게 부여한다.

103강, Spring Boot 프로젝트(fly, AWS 배포), JWT를 사용하여 인증

🔎 간략한 정리

  • validateToken, getDataFrom 메서드
    • 필터에서 사용 -> 키의 유효성 체크와 사용자 정보조회를 기존보다 빠르게
  • 60초 후에 로그인이 풀린다
    • 리프레시 토큰을 도입하여 해결 예정
  • UUID를 활용한 API_KEY 방식이 틀린게 아니다

104강, Spring Boot 프로젝트(fly, AWS 배포), Refresh Token 적용

🔎 간략한 정리

  • 필터에서 리프레시 토큰 활용
    • 엑세스 토큰이 만료 되었을 때
    • 리프레시 토큰을 사용하여 토큰을 재생성
  • 필터에서 Refresh Token 얻기
    • 엑세스 토큰이 만료되었다면 재갱신 시도
    • 재갱신이 되면 새로 엑세스 토큰을 쿠키로도 응답
      • 그래야 브라우저에 저장되어 있던 엑세스 토큰도 갱신됨

105강, Spring Boot 프로젝트(fly, AWS 배포), Refresh Token 작동방식

🔎 간략한 정리

  • 로그인을 한다 -> 쿠키가 생성된다
    • 엑세스 토큰과 리프레시 토큰
    • 로그인 후 모든 요청마다 엑세스 토큰과 리프레시 토큰은 요청의 헤더에 자동으로 포함
  • 토큰별로 검증에 걸리는 시간
    • 엑세스 토큰 : CPU 연산만으로 해당 토큰이 유효한지 알 수 있다
      • 유효성 체크 : 해당 토큰을 해커가 꾸민게 아닌 내(스프링부트)가 생성한것인지를 체크
    • 리프레시 토큰 : CPU 연산 + IO 연산(DB 쿼리)으로 해당 토큰이 유효한지 알 수 있다
      • 유효성 체크 : 해당 토큰을 해커가 꾸민게 아닌 내(스프링부트)가 생성한것인지를 체크
  • 각 토큰별 작동 타이밍
    • 엑세스 토큰 : 매 요청마다 해당 토큰이 사용됨
    • 리프레시 토큰 : 엑세스 토큰의 만료기간이 10분이라면, 개별 브라우저의 요청기준에서 10분에 1번씩만 사용됨
      • 이 특징때문에 리프레시 토큰이 엑세스 토큰과 비교해서 느려도 크게 문제되지 않음
      • -> 어차피 자주 사용 안함

106강, Spring Boot 프로젝트(fly, AWS 배포), SpringDoc

🔎 간략한 정리

  • OpenAPI 문서
    • 프론트엔드 개발자 입장에서 매우 편하게 서버 API에 대한 정보를 얻을 수 있다
    • OpenAPI 문서를 잘 만들면 POSTMAN을 대체할 수도 있다
  • SpringDoc
    • OpenAPI 표준에 맞게 문서를 자동을 만들어주는 툴이 Swagger
    • 스웨거를 스프링부트의 상황에 맞게 쓸 수 있게 해주는게 SpringDoc
  • SpringDocConfig
    • @OpenAPIDefinition : 스웨거 문서 세팅
    • @SecurityScheme : Authorization 헤더로 로그인하는 기능 활성화
    • @Bean public GroupedOpenApi groupApiV1() : API v1에 관련된 API에 대한 문서 생성
    • @Bean public GroupedOpenApi groupController() : API v1에 관련없는 API에 대한 문서 생성

107강, Spring Boot 프로젝트(fly, AWS 배포), 일반 Controller 문서 꾸미기

🔎 간략한 정리

  • OpenAPI 문서
    • 프론트엔드 개발자 입장에서 매우 편하게 서버 API에 대한 정보를 얻을 수 있다
    • OpenAPI 문서를 잘 만들면 POSTMAN을 대체할 수도 있다
  • SpringDoc
    • OpenAPI 표준에 맞게 문서를 자동을 만들어주는 툴이 Swagger
    • 스웨거를 스프링부트의 상황에 맞게 쓸 수 있게 해주는게 SpringDoc
  • SpringDocConfig
    • @OpenAPIDefinition : 스웨거 문서 세팅
    • @SecurityScheme : Authorization 헤더로 로그인하는 기능 활성화
    • @Bean public GroupedOpenApi groupApiV1() : API v1에 관련된 API에 대한 문서 생성
    • @Bean public GroupedOpenApi groupController() : API v1에 관련없는 API에 대한 문서 생성

108강, Spring Boot 프로젝트(fly, AWS 배포), REST Controller 문서 꾸미기

🔎 간략한 정리

  • api response body mime type

    • 모든 REST API 엔드포인트는 JSON 형태의 데이터 응답한다
    • 설정이 따로 없다면 /로 되어있음
  • produces = APPLICATION_JSON_VALUE

    • 기존 코드 : @RequestMapping("/api/v1/members")
    • 신규 코드 : @RequestMapping(value = "/api/v1/members", produces = APPLICATION_JSON_VALUE)
      • 명시적으로 해당 어노테이션이 붙은 컨트롤러 클래스의 모든 하위 엔드포인트 메서드의 응답 타입을 application/json으로 설정
      • 나중에 타입스크립트와 연동되어 프론트 개발자에게 도움이 됨
  • application.yml

    • springdoc: default-produces-media-type: application/json
    • 기본적인 응답 본문의 MIME 타입을 설정
  • GlobalExceptionHandler::handleException에 @ResponseStatus(HttpStatus.BAD_REQUEST)를 붙여서 실패응답 본문양식 설정


프론트엔드

트랜스 오버(실시간 번역), 크롬 익스텐션


스벨트


스벨트 5 preview


스벨트킷


프론트엔드 프레임워크별 문법 비교


사전학습기술

  • HTML
  • CSS
  • CSS, FLEX
  • 자바스크립트
  • 타입스크립트

사전학습기술 1 : HTML

사전학습기술 2 : CSS

사전학습기술 3 : CSS FLEX

사전학습기술 4 : 자바스크립트

사전학습기술 5 : 타입스크립트

Node.js 설치

NVM 명령어

  • nvm list
    • 현재 설치되어 있는 node.js 버전들 리스팅
  • nvm install v20.9.0
    • v20.9.0 설치
  • nvm use v20.9.0
    • 해당 node 버전 활성화

legacy-peer-deps 이 기본적으로 작동하도록 전역설정

npm config set legacy-peer-deps true # 이제 더 이상 --legacy-peer-deps 를 명시적으로 입력하지 않아도 된다.
npm config get legacy-peer-deps # 확인

npm 최신화

  • mac 에서는 아래 명령어 앞에 `sudo` 를 붙여야 할 수 도 있습니다.
npm install -g npm

109강, Spring Boot 프로젝트(fly, AWS 배포), 노드 설치 및 세팅, nvm, npm


110강, Spring Boot 프로젝트(fly, AWS 배포), VS CODE 설치 및 세팅


111강, Spring Boot 프로젝트(fly, AWS 배포), front 폴더를 VS CODE로 열기


112강, Spring Boot 프로젝트(fly, AWS 배포), 스벨트킷 프로젝트 생성

🔎 간략한 정리

  1. 스벨트킷과 스프링부트
  • 언어
    • 스벨트킷 : 자바스크립트
      • 우리는 타입스크립트로 할 예정
    • 스프링부트 : 자바
  • 프레임워크 구성
    • 스벨트킷 : 스벨트 + 추가기능
    • 스프링부트 : 스프링 + 추가기능
  • 프레임워크 성격
    • 스벨트킷 : 프론트엔드 프레임워크
      • 백엔드 프레임워크로서의 기능도 있음 / 대신 우리는 안 쓸 예정
    • 스프링부트 : 백엔드 프레임워크
      • 프론트엔드 프레임워크로서의 기능도 있음 / 대신 우리는 안 쓸 예정
  1. dependencies와 devDependencies의 차이
  • dependencies : 개발타임(컴파일타임), 런타임
  • devDependencies : 개발타임(컴파일타임)

113강, Spring Boot 프로젝트(fly, AWS 배포), 프로젝트 재생성 및 스벨트킷 실행

🔎 간략한 정리

  1. 스벨트킷 실행
  • npm run dev
    • npm run + 명령어 -> package.json의 scripts에 정의된 명령을 수행
  • http://localhost:5173이 기본
  • Ctrl + c 로 정지
  • 각 확장자 별로 프리티어 활성화
  • 전체 소스코드 정리
    • npm run format : 인텔리제이에서 Reformat code 한거랑 같은 기능

114강, Spring Boot 프로젝트(fly, AWS 배포), 라우팅

🔎 간략한 정리

  1. http://localhost:5173
  • 메인페이지
  • routes/+page.svlete
  1. http://localhost:5173/about
  • 설명 페이지
  • routes/about/+page.svlete
  1. http://localhost:5173/home/links
  • 링크들
  • routes/home/links/+page.svlete

115강, Spring Boot 프로젝트(fly, AWS 배포), 레이아웃

🔎 간략한 정리

  1. 전체 레이아웃
  • routes/+layout.svelte
    • 모든 페이지에 적용됨
  1. 특정 범위 레이아웃
  • routes/about/+layout.svelte
    • 설명 페이지(routes/about/+page.svelte)에만 적용됨
  1. 결론 : 레이아웃은 중첩 적용이 가능하고 같은 폴더내에만 적용된다.

116강, Spring Boot 프로젝트(fly, AWS 배포), 로그인 페이지


117강, Spring Boot 프로젝트(fly, AWS 배포), CORS 허용

🔎 간략한 정리

  1. CORS 허용
  • 타 도메인간 fetch는 기본적으로 금지여서
  • CORS 작동은 브라우저, 허용은 백엔드

118강, Spring Boot 프로젝트(fly, AWS 배포), 크로스 도메인 쿠키 허용

🔎 간략한 정리

  1. 크로스 도메인 쿠키 허용
  • credentials: 'include' : 브라우저에서 fetch를 할 때 꼭 해당 옵션을 붙여야한다.
  1. 크로스 도메인 쿠키 허용 백엔드
  • .domain(getSiteCookieDomain()) : 프론트 환경의 도메인을 명시해야 한다.
  1. 크로스 도메인 쿠키에 대한 보안 설정
  • .sameSite("Strict") : 특정한 환경에서만 해당 쿠키가 접근할 수 있도록
  • .secure(true) : 안전한 환경(HTTPS)에서만 쿠키 전송이 되도록
  • .httpOnly(true) : 자바스크립트를 통한 쿠키 접근을 금지하도록, XSS 공격으로 인한 쿠키 탈취를 방어

119강, Spring Boot 프로젝트(fly, AWS 배포), 백엔드, 프론트엔드에서 개발,운영환경 도메인 분기


120강, Spring Boot 프로젝트(fly, AWS 배포), Refresh Token 생성로직 변경

🔎 간략한 정리

  • initAll에서 회원의 Refresh Token 변경
    • 운영환경이 아니라면 username 과 refreshToken 같도록 설정
  • Refresh Token 생성로직 변경
    • UUID는 너무 길다.
    • 별도의 로직으로 조금 더 짧게 생성되도록 변경

121강, Spring Boot 프로젝트(fly, AWS 배포), OPEN API 문서에서 로그인

🔎 간략한 정리

  • @SecurityRequirement
    • API 문서에서 로그인 하면 그것이
    • API 문서에서 Try out 할 때 적용되도록
    • @SecurityRequirement를 API 컨트롤러에 적용
  • Filter에서 쿠키보다 헤더를 통한 로그인을 우선시
    • 기타 개선작업
  • API 문서에서 로그인 테스트
  • 내 정보 조회 API 구현
    • 현재 로그인한 회원의 정보를 반환

🔎 간략한 정리

  1. 개발환경
  1. 운영환경
  1. 소스코드에서의 도메인 명시 (하드코딩 x)
  • 프론트엔드 도메인 : AppConfig.getFrontUrl();
  • 백엔드 도메인 : AppConfig.getBackUrl();
  • 쿠키도메인 : AppConfig.getSiteCookieDomain();

122강, Spring Boot 프로젝트(fly, AWS 배포), 로그아웃

🔎 간략한 정리

  • logout 함수 구현
    • +layout.svelte 페이지에서 로그아웃 버튼 누르면 logout 함수 실행
  • 백엔드 로그아웃 로직 변경
    • accessToken, refreshToken 쿠기 삭제로 변경
  • 로그아웃 요청은 시큐리티에서 로그인 체크
    • DELETE /api/v1/members/logout 요청도 로그인 체크 대상에서 제외

123강, Spring Boot 프로젝트(fly, AWS 배포), 내 정보 페이지 구현

🔎 간략한 정리

  • 내 정보 페이지 구현
    • routes/member/me/+page.svelte 파일 추가
  • GlobalError 커스텀 예외 클래스 추가
  • type Member 정의
  • member 상태변수 정의
    • 상태변수는 `$state(초기값); 형태로 초기화된 변수이다.
      • 보통 뷰에서 사용되는 변수이면 상태변수로 만드는게 보통이다.
    • `let member: Member | null = $state(null);
  • errorMessage 상태변수 정의
    • `let errorMessage: string | null = $state(null);
  • getMe 함수 구현
    • 해당 함수에서 API 요청
    • 성공하면 member 변수에 값 할당
      • 해당 변수가 뷰단에서 사용된 상태변수이기 때문에 해당 변수에 값을 할당하는 것 만으로 뷰 부분이 자동으로 갱신됨
    • 실패하면 errorMessage 변수에 값 할당
      • 해당 변수가 뷰단에서 사용된 상태변수이기 때문에 해당 변수에 값을 할당하는 것 만으로 뷰 부분이 자동으로 갱신됨
  • $effect 에서 getMe 함수 실행
    • 자동실행되어야 하는 함수는 $effect 안쪽에서 호출하는게 스벨트의 룰이다.
  • svelte.config.js는 왜 수정이 필요한가?
    • 기본 설정(vitePreprocess())은 TypeScript를 처리하지만, Svelte 컴파일러에서 script 태그 내에서 TypeScript의 특정 기능(public, private 등 접근 제한자)을 처리할 수 있도록 명시적으로 활성화해야 합니다.
    • vitePreprocess({ script: true })를 추가하면, TypeScript의 모든 기능이 <script> 태그 내부에서 정상적으로 작동합니다.

124강, Spring Boot 프로젝트(fly, AWS 배포), 백엔드 통신용 타입스크립트 자동생성

🔎 간략한 정리

  • Dev 클래스 추가
    • initDev 빈 정의
    • 오직 개발환경에서만 실행됨
    • 해당 빈의 코드는 소스코드를 수정하거나
    • 스프링 부트를 실행하거나 스프링 부트를 재실행 하면 자동으로 수행된다.
  • initDev 빈의 작동
    • OPEN API에서 apiV1 문서를 JSON 형태로 읽어서
    • 그것을 기반으로 타입스크립트를 만들어준다.
    • 해당 타입스크립트는 프론트엔드에서 백엔드로 요청하는 코드에서 사용될 예정이다.
  • 명령어를 쉘에서 실습
    • npx openapi-typescript http://localhost:8070/v3/api-docs/apiV1 -o ./front/src/lib/backend/apiV1/schema.d.ts

125강, Spring Boot 프로젝트(fly, AWS 배포), 로그인 요청에 openapi-fetch 적용

🔎 간략한 정리


126강, Spring Boot 프로젝트(fly, AWS 배포), 로그아웃, 내 정보 요청에 openapi-fetch 적용

🔎 간략한 정리

  • 로그인 후 goto를 통해서 메인페이지로 이동
  • 로그아웃 요청에 openapi-fetch 적용
  • 로그아웃 후에 goto를 통해서 메인페이지로 이동
  • 내 정보에 요청에 openapi-fetch 적용
    • 응답본문객체의속성과 MemberDto 타입스크립트 객체의 속성에 undefined 타입 가능성이 있어서 사용하기 불편함
    • @NonNull 을 붙이면 되는데, @NonNull 이 없으면 if 문을 더 많이 써야 함
    • 추후 개선할 예정

127강, Spring Boot 프로젝트(fly, AWS 배포), 자바 DTO에 @nonnull 적용

🔎 간략한 정리

  • 결국 통신을 위한 모든 타입스크립트는 스웨거에 의해서 생성됨
  • 자바 DTO의 모든 필드에 @NonNull 적용
    • @NonNull 이 없으면 if 문을 더 많이 써야 함면 되는데, @NonNull 이 없으면 if 문을 더 많이 써야 함

128강, Spring Boot 프로젝트(fly, AWS 배포), 스벨트용 Rq 클래스

🔎 간략한 정리

  • svelte.ts 라는 파일명으로 끝나는 이유는 추후 여기에 상태변수를 정의하고 공유할 예정이기 때문이다.
  • rq 객체는 최초에 브라우저로 이 앱에 진입할 때 딱 1번만 생성된다. 일종의 싱글톤 객체라고 생각해도 된다.

129강, Spring Boot 프로젝트(fly, AWS 배포), rq.member에 로그인 정보 저장

🔎 간략한 정리

  • HTTP ONLY 쿠키는 자바스크립트(스벨트)에서 절대 접근할 수 없어서 따로 rq.member 를 따로 두어서 관리해야 한다.
  • 파일 명이 .svlte.ts 로 끝나는 이유는 $state를 사용하기 위함이고, 회원객체의 구성요소는 전부 상태변수로 만들어야 한다.
  • Rq 클래스에 로그인한 회원정보 저장할 수 있도록 작업
  • 로그인 후 rq.member 변수 로드
  • 레이아웃의 메뉴에서 로그인 여부에 따라 분기
    • 로그인 했을 때의 메뉴 : 로그아웃, 내 정보
    • 로그아웃 했을 때의 메뉴 : 로그인
  • 웹 페이지 최초 로딩 후 rq.member 변수 로드
    • routes/+layout.svelte 에서 수행
  • rq.member 를 내 정보 페이지에서 사용
  • 로그아웃 후 rq.member 변수 언로드

130강, Spring Boot 프로젝트(fly, AWS 배포), 회원가입

🔎 간략한 정리

  • 회원가입
  • 회원가입 후 로그인 페이지로 이동
  • 레이아웃의 메뉴에서 로그인한 회원이름 노출

125일차

131강, Spring Boot 프로젝트(fly, AWS 배포), SURL 리스트, 등록

🔎 간략한 정리

  • surls 변수는 상태변수여야 합니다. 그래야 추후 해당 배열에서 요소가 삭제될 때 관련 뷰가 자동갱신 됩니다.
  • rq.replace 로 이동하면, 이후에 뒤로가기를 통해서 원래 페이지로 돌아갈 수 없습니다.

132강, Spring Boot 프로젝트(fly, AWS 배포), SURL 삭제, 상세페이지

🔎 간략한 정리

  • confirm 함수는 사용자에게 yes or no 를 물어보고, 그것에 따라 true or false를 리턴하는 유용한 함수 입니다.
  • URL /surl/5 와 같은 식의 URL로 하시려면 /routes/surl/[id]/+page.svelte 형태로 파일을 만들어야 합니다.
  • /surl/5 에서 id 값 5 라는 값은 $page.params.id 로 접근해서 얻을 수 있습니다.

133강, Spring Boot 프로젝트(fly, AWS 배포), SURL 수정, 원본 URL로 이동

  1. my-app 폴더에 vercel.json 파일 생성
  2. vercel.json에 아래 코드 추가(node 버전 명시)
{
	"build": {
		"env": {
			"NODE_VERSION": "20"
		}
	}
}
  1. vercel에서 해당 프로젝트 누르고 Settings 클릭
  2. 좌측의 General 클릭
  3. Node.js Version을 20.x로 선택
  4. Redeploy 클릭
  5. 배포 끝나면 Settings -> Domains -> 아래 사진 빨간박스에 프론트엔드 전용 URL입력 후 Add 클릭
  6. DNSZi 페이지 로그인 -> 해당 도메인 클릭 후 CNAME 관리 클릭(빨간 박스)
  7. 아래 사진의 ①과 ②는 같은값이 들어있어야한다. 맞게 됐다면 CNAME 추가 클릭

  8. 배포 완료

🔎 간략한 정리

  • hooks.server.ts 파일은 스프링부트 필터의 스벨트킷 버전이라고 볼 수 있습니다.
profile
우당탕탕....

0개의 댓글