본문 바로가기
프로젝트들/스프링부트와 AWS 웹서비스

[project] 스프링부트와 AWS로 혼자 구현하는 노트정리

by 커피는아아 2020. 11. 8.
반응형

스프링부트와 AWS로 혼자 구현하는 웹서비스

Service URL: https://bit.ly/2UvbHkD

 

Feedback Service

 

ec2-3-34-29-36.ap-northeast-2.compute.amazonaws.com

Springboot-AWS 프로젝트는 아래의 기능을 연습하기 위한 프로젝트입니다.

  • Intellij 사용하기
  • github로 버전관리
  • Spring Boot를 활용한 Spring 프로젝트 생성하기
  • Spring Boot에서 테스트코드 작성하기
  • Spring Data JPA를 활용한 관계형 데이터베이스 엑세스하기
  • H2 인메모리 관계형 데이터베이스 사용하기
  • @RestController를 활용한 REST API 제공하기
  • Mustache를 템플릿 엔진으로 사용하기
  • Spring Security와 OAuth 2.0으로 소셜로그인 구현하기
  • AWS EC2 (서버환경) , AWS RDS (데이터베이스(MariaDB) 환경) 구축하기
  • AWS EC2 서버에 프로젝트 배포하기
  • Travis CI 배포 자동화 구현
  • 24/7 무중단 배포 서비스 구현

0. Basic infomation

Intellij (인텔리제이)

  • 강력한 추천 기능(Smart Completion)
  • 훨씬 더 다양한 리팩토링과 디버깅 기능
  • 이클립스의 깃에 비해 훨씬 높은 자유도
  • 프로젝트 시작할 때 인덱싱을 하여 파일을 비롯한 자원들에 대한 빠른 검색 속도

이클립스와의 차이점

  • workspace가 없다
  • Project와 Module의 개념이 존재
  • RootProject아래에 Multi module환경을 이용하면 된다
    Multi Module이란 간단하게 하나의 도메인을 위한 프로젝트 모음

bit.ly

  • bitly로 줄인 URL을 블로그나 사이트에 게시했을 때 조회수도 확인 가능하다.
  • URL을 짧게 줄여준다.

Gist

  • 코드조각(Code Snippset), 로그, 메모 등을 남기는데 사용 .
  • Code Snippset 이용하여 blog , notion에 업로드에 사용

Annotation

  1. @SpringBootApplication

    • 스프링부트의 자동 설정, 스프링 Bean읽기와 생성을 모두 자동으로 설정
  2. @RestController

    • 컨트롤러를 JSON을 반환하는 컨트롤러로 만들어 준다
    • @ResponseBody를 각 메소드마다 선언했던 것을 통합한 것
  3. @GetMapping

    • HTTP Method인 Get의 요청을 받을수 있는 API
    • 예전 @RequestMapping(method = RequestMethod.GET)
  4. @RunWith(SpringRunner.class)

    • 테스트진행할 때 JUnit에 내장된 실행자 외에 다른 실행자를 실행시킨다
    • 여기선 SpringRunner라는 스프링 실행자를 실행
    • 스프링부트 테스트와 JUnit 사이에 연결자 역할
  5. @SpringBootTest

    • 별다른 설정없이 이것을 사용할 경우 H2 데이터베이스를 자동으로 실행
  6. @WebMvcTest

    • 여러 스프링 테스트 어노테이션 중, Web(Spring MVC)에 집중할 수 있는 어노테이션
    • 선언할 경우 @Controller, @ControllerAdvice등을 사용가능
    • 단 @Service, @Component, @Repository 등은 사용불가
  7. @Autowired

    • 스프링이 관리하는 빈(Bean)을 주입
  8. @Getter(롬복)

    • 선언된 모든필드의 get메소드를 생성해 준다
  9. @RequiredArgsConstructor(롬복)

    • 선언된 모든 final 필드가 포함된 생성자를 생성해 준다
    • final이 없는 필드는 생성자에 포함되지 않는다
    • @NonNull이나 final필요
  10. @NoArgsConstructor(롬복)

    • 파라미터가 없는 기본생성자 자동추가
    • public Test() {}와 효과
  11. @Builder(롬복)

    • 해당 클래스의 빌더 패턴 클래스를 생성
    • 생성자 상단에 선언 시 생성자에 포함된 필드만 빌더에 포함
    • 생성자와 같이 생성시점에 값을 채워주는 역할은 같으나 지금 채워야할 필요가 무엇인지 명확히 지정할 수 있다
    public Example(String a, String b) {
        this.a = a;
        this.b = b;
    }
    new Example(b,a)해도 코드를 실행하기 전까지는 문제를 발견 할 수 없다
    
    Example.builder()
                    .a(a)
                    .b(b)
                    .build();
  12. @Entity(JPA)

    • 테이블과 링크될 클래스
    • 기본값으로 클래스의 카멜케이스 이름을 언더스코어 네이밍(_)으로 테이블 이름을 매칭한다.
    • ex) SalesManager.java -> sales_manager_table
  13. @Id(JPA)

    • 해당 테이블의 PK 필드를 나타낸다
  14. @GeneratedValue(JPA)

    • PK의 생성 규칙을 나타냅니다
    • 스프링 부트 2.0에서는 GenerationType.IDENTITY 옵션을 추가해야만 auto increment가 된다
  15. @Column(JPA)

    • 테이블의 컬럼, 선언하지 않더라도 해당 클래스의 필드는 모두 칼럼이 된다
    • 기본값 외에 추가로 변경이 필요한 옵션이 있으면 사용한다.
    • 문자열의 VARCHAR(255) 값을 500으로 늘리고 싶다거나 타입을 TEXT로 변경할 때 사용
  16. @After(Junit)

    • Junit에서 단위 클래스가 끝날 때마다 수행되는 메소드를 지정
    • 보통은 배포전 전체 테스트를 수행할 때 테스트간 데이터 침범을 막기위해 사용
    • 여러 테스트가 동시에 수행되면 H2에 데이터가 그대로 남아있어 실패할 가능성 有
  17. @MappedSuperclass(JPA)

    • JPA Entity 클래스들이 BaseTimeEntity를 상속할 경우 필드들 (createdDate, modifiedDate)도 컬럼으로 인식한다.
  18. @EntityListeners(AuditingEntityListener.class)(JPA)

    • BaseTimeEntity 클래스에 Auditing 기능을 추가한다.
  19. @CreatedDate(JPA)

    • Entity가 생성되어 저장될 때 시잔이 자동으로 저장된다.
  20. @LastModifiedDate(JPA)

    • 조회한 Entity의 값을 변경할 때 시간이 자동 저장
  21. @EnableJpaAuditing(JPA)

    • Jpa auditing 활성화
  22. @Transactional(readOnly = true)

    • 트랜잭션 범위는 유지하되 조회 기능만 남겨두어 조회 속도가 계산된다.
  23. @Enumerated(EnumType.STRING)

    • jpa로 데이터베이스를 저장할 때 Enum값을 어떤 형태로 저장할지를 결정한다.
    • 기본값 int
    • 숫자로 저장되면 데이터베이스로 확인할 때 그 값이 무슨 코드를 의미하는지 알 수 가 없다.
    • 그래서 문자열(EnumType.STRING)으로 저장될 수 있도록 선언한다.
  24. @Target(ElementType.PARAMETER)

    • 이 어노테이션이 생성될 수 있는 위치를 지정한다.
    • PARAMETER로 지정했으니 메소드의 파라미터로 선언된 객체에서만 사용할 수 있다
  25. @interface

    • 이파일을 어노테이션 클래스로 지정한다.
    • LoginUser라는 이름을 가진 어노테이션이 생성되었다
  26. @Retention(RetentionPolicy.RUNTIME)

    • 컴파일 이후에도 JVM에 의해서 참조가 가능합니다.

Code Description

  1. mvc.perform(get("/hello"))

     mvc.perform(get("/hello"))
     - MockMvc를 통해 /hello 주소로 HTTP GET 요청
     - 체이닝을 지원
    
     .andExpect(status().isOk())
     - mvc.perform의 결과를 검증
     - HTTP Header의 Status를 검증
     - 200, 400, 500등의 상태 검증
     - OK 즉 200인지 아닌지를 검증
    
     .andExpect(content().string(hello));
     - mvc.perform의 결과를 검증
     - 응답 본문의 내용을 검증
     - Controller에서 "hello"를 리턴하는지를 검증한다.
    
     .param
         - API테스트할 때 사용될 요청 파라미터를 설정한다.
         - 값은 String만 허용
         - 숫자/날짜 등의 데이토도 등록할 때는 문자열로 내려보내야 한다.
    
     jsonPath()
         .andExpected(jsonPath("$.name", is(name)))
         - JSON 응답값을 필드별로 검증할 수 있는 메소드
         - $를 기준으로 필드명을 명시한다.
  2. assertThat (assertj)

    • Junit의 assertThat이 아닌 assertj 것을 사용하는 이유

      1. CoreMatchers 라이브러리가 필요하지 않다
      2. 자동완성이 보다 확실하게 지원된다
      import static org.assertj.core.api.Assertions.assertThat;
    • assertj라는 테스트 검증 라이브러리 메소드

    • 검증하고 싶은 대상을 인자로 받는다

    • 메소드 체이닝이 지원되어 isEqualTo와 같이 메소드를 이어서 사용가능

    • ex) assertThat(dto.getName()).isEqualTo(name);

    • isEqualTo

      • assertj의 동등 비교 메소드
      • assertThat에 있는 값과 isEqualTo의 값을 비교해서 같을 때만 성공
  3. isAfter() ← LocalDateTime의 메소드 1.isAfter(2) 1이 2보다 뒤에있는 것인지 판단


1. 인텔리제이로 스프링부트 시작하기

  • 인텔리제이를 비롯한 각종 IDE관리하는 툴박스
  • 인텔리제이의 설치와 기본사용법
  • mavenCentral, jcenter비교
  • 스프링부트 프로젝트와 그레이들 연동방법
  • 인텔리제이에서 깃허브 사용하는법

Maven vs Gradle

Build란?

  • 소스코드 파일을 컴퓨터에서 실행할 수 있는 독립 소프트웨어 가공물로 변환하는 과정 또는 그에 대한 결과물 이다.
  • 우리가 작성한 소스코드(java), 프로젝트에서 쓰인 각각의 파일 및 자원 등(.xml, .jpg, .jar, .properties)을 JVM이나 톰캣같은 WAS가 인식할 수 있는 구조로 패키징 하는 과정 및 결과물이라고 할 수 있다.

Build tool

  • 빌드 도구란 프로젝트 생성, 테스트 빌드, 배포 등의 작업을 위한 전용 프로그램.
  • 빠른기간동안 계속해서 늘어나는 라이브러리 추가, 프로젝트를 진행하며 라이브러리의 버전 동기화의 어려움을 해소하고자 등장.
  • 초기의 java 빌드도구로 Ant를 많이 사용하였으나 최근 많은 빌드도구들이 생겨나
    Maven이 많이 쓰였고, 현재는 Gradle이 많이 쓰인다.
    (Ant는 스크립트 작성도 많고, 라이브러리 의존관리가 되지 않아 불편함)

1.1 build.gradle 설정

 

 

1.2 인텔리제이에서 깃허브로 버전관리하기

  • .idea디렉토리는 커밋하지 않는다.

    • .idea: 인텔리제이에서 프로젝트 실행시 자동으로 생성되는 파일
  • .gitignore 파일 사용

    • 깃 체크대상에서 제외를 시킬 수 있다.

       

       


2. 스프링부트에서 테스트코드 작성

  • TDD와 단위테스트란 무엇인가
  • 스프링부트 환경에서 테스트코드를 작성하는 방법
  • 자바의 필수 유틸 롬복(lombok)의 사용법

2.1 테스트 코드란

TDD

  • 테스트가 주도하는 개발
  • 테스트코드를 먼저작성한다.
  • Red → Green → Refactor 사이클

단위 테스트 코드 (Unit test)

TDD 첫번째 단계인 기능 단위의 테스트 코드를 작성하는 것

장점

  1. 빠른 피드백
    • 기존방식
      1. 코드작성
      2. Tomcat 실행
      3. Postman과 같은 API 테스트도구로 HTTP 요청
      4. System.out.println()으로 눈으로 검증
      5. 결과 다르면 Tomcat중지하고 코드 수정
    • 코드수정시마다 톰캣을 다시 실행하는 시간을 줄일 수 있다.
  2. 자동검증
    • System.out.println()을 찍으며 수동검증 할 필요가 없어진다.
  3. 개발자가 만든 기능을 안전하게 보호 해준다.
    • 기존 기능이 잘 작동되는 것을 보장해 준다.

테스트 코드 프레임워크 xUnit

  • JUnit4 -java용 사용

2.2 Hello Controller 테스트코드 작성하기

Application.java

 

 

HelloController.java, HelloControllerTest.java

 

 

 

 

2.3 롬복 lombok

소개

  • Getter, Setter, 기본생성자 toString등을 어노테이션으로 자동 생성
  • 롬복은 프로젝트마다 설정하여야 한다.

설치

  1. build.gradle의 의존성 주입
  2. 롬복 플러그인 설치 Enable annotation processing 체크

2.4 HelloController 코드를 롬복으로 리팩토링

HelloResponseDto, HelloResponseDto Test

 

 

 

 


3. 스프링부트에서 JPA로 데이터 베이스 다루기

  • JPA/Hiibernate/Spring Data Jpa의 관계
  • Spring Data Jpa를 이용하여 관계형 데이터베이스를 객체지향적으로 관리하는 방법
  • JPA 더티체킹을 이용하면 Update 쿼리 없이 테이블 수정이 가능하다는 것
  • JPA Auditing을 이용하여 등록/수정 시간을 자동화하는 방법

JPA(Java Persistent API)

  • JPA는 자바표준 ORM(Object Relation Mapping)이다.
  • JPA는 객체지향 프로그래밍 언어와 관게형 데이터베이스의 패러다임 불일치 문제를 해결하기 위해 사용한다.
  • 개발자는 객체지향적 방법으로 프로그래밍을 하고, JPA가 관계형데이터베이스에 맞게 SQL을 생성해서 실행한다.
  • JPA를 사용하면 SQL에 종속적인 개발을 하지 않아도 된다.

Spring Data JPA

  • JPA는 자바 표준 명세서다.
  • JPA는 Hibernate, Eclipse Link 등의 대표적인 구현체가 있다.
  • Spring에서는 구현체들을 추상화시킨 Spring Data JPA를 이용해서 JPA 기술을 다룬다.
    • Spring Data JPA ← Hibernate ← JPA ← DataBase
  • 등장 배경
    1. 구현체 교체의 용이성
      • Hibernate 외의 다른 구현체로 쉽게 교체하기 위함
    2. 저장소 교체의 용의성
      • 관계형 데이터베이스 외에 다른 저장소로 쉽게 교체하기 위함
      • ex) MongoDB로 교체가 필요하면 Spring Data JPA → Spring Data MongoDB로 의존성만 교체
        Spring Data의 하위 프로젝트는 CRUD의 인터페이스가 같기 때문
        Spring Data Redis 등등

프로젝트에 Spring Data JPA 적용하기

  1. spring-boot-starter-jpa 의존성 등록

    • 스프링 부트용 Spring Data Jpa 추상화 라이브러리
    • 스프링 부트 버전에 맞춰서 자동으로 JPA관련 라이브러리들의 버전관리를 해준다.
  2. H2 인메모리 관계형 데이터베이스 사용

    • 프로젝트 의존성만으로 관리 가능
    • 메모리에서 실행되기 때문에 애플리케이션 재시작시 초기화 된다는 점을 이용하여 테스트에서 주로 사용
  3. 관계형 데이터베이스의 테이블과 매핑되는 Entity클래스 작성하기

    • domain 패키지

      • 게시글, 댓글, 회원, 결제 등 소프트웨어에 대한 요구사항 혹은 문제영역 dao패키지와 비슷하나 결이 다르다
    • Posts.java

       

       

  4. 데이터베이스 엑세스를 담당하는 JpaRepository 생성하기

    • PostsRepository.java

       

       

    • Mybatis에서 Dao라 불리는 DB Layer 접근자

    • Entity 클래스와 Entity Repository는 함께 위치해야 한다

    • @Repository를 추가할 필요가 없다

    • T: Entity클래스, ID: PK타입

    • JpaRepository<T, ID> 인터페이스를 상속받는 인터페이스를 정의한다.

    • JpaRepository<T, ID> 인터페이스를 상속받으면 기본적인 CRUD 메소드가 자동으로 생성된다.

  5. Spring Data JPA 테스트 코드 작성하기

    • PostsRepositoryTest.java 생성 Save findAll기능을 수행

       

       

  6. DataSource 설정하기 & 쿼리 로그보기

    • H2 인메모리 데이터베이스에 대한 DataSource 설정하기

    • application.properties

       

       

Spring Boot와 REST

REST

  • REST(Respresentational State Transfer)는 HTTP를 활용해서 URL에 해당하는 자원의 상태(정보)를 주고받는 것이다.
  • REST의 구성
    • 자원(Resource) - URI
    • 행위(Verb) - HTTP Method (GET, POST, PUT, DELETE)
    • 표현(Representations) - 해당 자원을 표현하는 적절한 이름(students, users, products, devices 등)

REST API 디자인

  • 첫번째, URI는 정보의 자원을 표현해야 한다.
  • 두번째, 자원에 대한 행위는 HTTP Method(GET, POST, PUT, DELETE)로 표현한다.
  • com.example.posts.web.PostsAPIController.java 참조

REST API 작성예

Spring Bean 주입 방식

  • @Autowired, setter, 생성자(권장방식)
    • 생성자로 Bean객체를 받도록 하면 @Autowired와 동일한 효과
    • RequiredArgsConstructor (롬복) 이 해결해준다
    • final이 선언된 모든 필드를 인자값으로 하는 생성자를 생성한다.

등록 수정 삭제 API 구현

  • PostsApiController

     

     

  • PostsService

     

     

    JPA 영속성 컨텍스트
    • update 기능에서 쿼리를 날리는 부분이 없다
    • 엔티티를 영구 저장하는 환경
    • JPA의 핵심은 엔티티가 영속성 컨텍스트에 포함되아 있냐 아니냐로 갈린다.
    • 트랜재션 안에서 db에서 데이터를 가져오면 이 데이터는 영속성 컨텍스트가 유지된 상태
    • 트랜잭션이 끝나는 시점에 해당 테이블에 변경분을 반영
    • 즉 Entity 객체의 값만 변경되면 별도로 update 쿼리를 날릴 필요가 없다. 더티체킹
  • PostsSaveRequestDto

    Entity 클래스(Posts)와 유사해도 Dto 클래스를 추가로 생성한 이유

    • Entity클래스는 DB와 맞닿은 핵심 클래스

    • 이를 기준으로 테이블 생성 및 스키마 변경 된다. 여러 클래스에 영향을 끼친다

    • Controller에서 쓸 Dto는 분리해서 사용해야 한다.

       

       

  • PostResponseDto

     

     

  • PostUpdateRequestDto

     

     

  • Posts

     

     

  • PostsApiControllerTest

     

     

JPA Auditing (생성시간 / 수정시간 자동화)

  • LocalDateTime (Java8 일 경우 반드시 사용)

    • java8이전 Date와 Calendar 클래스의 문제점
      1. 불변 객체가 아니다 (멀티스레드 환경에서 문제가 발생 할 수 있다)
      2. Calendar 월 (Month) 값 설계가 잘못 되어있다. ex) Calendar.OCTOBER 값이 '9'
  •  
  • BaseTimeEntity

     

     

  • Application

     

     

  • PostsRepositoryTest

     

     


4. Spring Boot와 웹 애플리케이션(Mustache 템플릿 엔진 사용)

  • 서버 템플릿 엔진과 클라이언트 템플릿 엔진의 차이
  • 머스테치의 기본 사용방법
  • 스프링 부트에서의 화면 처리 방식
  • js/css 선언 위치를 다르게 하여 웹사이트의 로딩속도를 향상하는 방법
    • HTML은 위에서 부터 읽어오기 때문에 화면을 그리는 css는 헤드 부분에(css가 적용되지 않은 화면을 보지 않기 위해)
    • js는 header에 있을 경우 용량이 커질 수록 body의 실행이 늦어지기 때문에 하단에 둔다.
    • bootstrap.js는 jquery가 있어야만 하기 때문에 jquery를 먼저 호출한다.
  • js 객체를 이용하여 브라우저의 전역변수 충돌 문제를 회피하는 방법

서버 템플릿 엔진

  • 서버 템플릿 엔진은 지정된 템플릿과 데이터를 합쳐서 동적 HTML 문서를 생성하는 소프트웨어다.
  • 대표적인 서버 템플릿 엔진
    • JSP, Freemarker, Thymeleaf, Mustache 등이 있다.

Mustache 템플릿 엔진

  • 다양한 프로그래밍 언어를 지원하는 가장 심플한 템플릿 엔진이다.
  • 문법이 간단하다.
  • 로직요소를 사용할 수 없기 때문에 View역할과 Logic역할을 명확하게 분리할 수 있다.

프로젝트에 Mustache 템플릿 엔진 사용하기

  1. spring-boot-starter-mustache 의존성 등록하기

  2. src/main/resources/templates 폴더에 템플릿 파일 정의하기

    • src/main/resources/templates와 src/main/resources/templates/layout 폴더의 *.mustache 파일 참조
  3. @Controller을 이용해서 PostsController 생성하기

    • com.example.posts.web.PostsController.java 참조
  4. 사용자와 상호작용하는 스크립트 파일 생성하기

    • src/main/resources/static/js/app/index.js /

    • var main = {} 변수속성으로 function을 추가한 이유

      1. main객체 안에서만 function이 유효하기 때문에 다른 JS와 겹칠 위험이 사라진다
      2. 브라우저의 스코프는 공용공간이기 때문에 중복된 함수 이름의 중복을 없애기 위함이다.
    • 스프링 부트에서는 src/main/resources/static에 위치한 자바스크립트,CSS, 이미지 등 적정파일들은 URL에서 /로 설정된다.
      그래서 다음에 위치시키면 위치에 맞게 호출이 가능하다
      footer.mustache 참조

    • indexController.java

       

       

    • index.js

       

       

  • footer.mustache에 index.js를 추가

     

     

  • localhost:8080/h2-console에 접속해서 실제 DB에 데이터가 등록되었는지 확인

  • index.mustache

     

     

  • PostsRepository 인터페이스의 쿼리 추가

     

     

    • 규모가 있는 프로젝트에서의 데이터 조회는 복잡하기 때문에 Entity클래스만으로 처리하기 어려울 수 있다

    • 따라서 조회용 프레임워크를 따로 사용한다 ( querydsl, jooq, Mybatis)

    • 조회는 프레임워크를 통해 조회하고, 등록/수정/삭제 등은 SpringDataJpa를 통해 진행한다

    • Querydsl 추천이유

      1. 타입 안정성이 보장
        • 단순한 문자열로 쿼리를 생성하는 것이 아니라 메소드를 기반으로 쿼리를 생성하기 때문에 오타나 존재하지 않는 컬럼명을 명시할 경우 IDE에서 자동으로 검출된다. Mybatis는 지원 x
      2. 국내 많은 회사에서 사용 중
    • PostService.java

       

       

      • PostsApiController.java

         

         

스프링 시큐리티와 OAuth2.0 클라이언트 로그인 기능 구현

  • 스프링 부트 1.5와 스프링 부트 2.0에서 시큐리티 설정의 차이점
  • 스프링 시큐리트를 이용한 구글/네이버 로그인 연동방법
  • 세션 저장소로 톰캣/데이터베이스/메모리 DB가 있으며 이 중 DB를 사용하는 이유
  • ArgumentResolver를 이용하면 어노테이션으로 로그인 세션 정보를 가져올 수 있는 것
  • 스프링 시큐리티 적용 시 기존 테스트 코드에서 문제 해결 방법

스프링 스큐리티

  • 막강한 인증과 인가 기능을 가진 프레임워크
  • 스프링 기반의 애플리케이션에서는 보안을 위한 표준

스프링부트 1.5 vs 스프링 부트 2.0

  • 스프링부트 1.5와 Oauth2 연동방법이 2.0에서는 크게 변경되었다
  • spring-security-oauth2-autoconfigure 덕분이다.
  • 스프링부트1.5에서 사용하던 spring-security-oauth 프로젝트는 더이상 개선되지 않는다
  • 스프링 부트 스타터 라이브러리 출시 (spring-security-oauth2-client와 spring-security-oauth2-jose를 기본으로 관리 해준다)
  • spring-security-oauth2-autoconfigure 라이브러리 사용 유무와 application.properties, application.yml 입력 방식이 차이가 난다
  • 스프링부트2.0에서는 url 주소를 모두 명시하지 않아도 되며 client인증정보만 입력하면 된다.
  • 직접 입력했던 값들은 CommonOAuth2Provider 라는 enum으로 모두 대체되었다.

Oauth2 클라이언트를 사용하는 이유

  • 로그인시 보안, 회원가입시 이메일혹은 전화번호 인증 절차 비밀번호 찾기, 변경 회원정보 변경 등 로그인 구현시 필요 한 것들을 위임하여 서비스 개발에 집중할 수 있게 된다.

구글 서비스 등록

  1. 구글 클라우드에서 프로젝트 생성 및 사용자 인증정보 만들기

  2. 구현할 소셜 로그인 OAuth 클라이언트 ID로 구현

  3. OAuth 클라이언트 ID 만들기

  4. 승인된 리디렉션 URI

    • 서비스에서 파라미터로 인증 정보를 주었을 때 인증이 성공하면 구글에서 리다이렉트할 URL
    • 스프링 부트 2버전에 시큐리티에서는 기본적으로 {domain}/login/oauth2/code/소셜서비스코드로 리다이렉트 URL로 지원
    • 사용자가 별도로 리다이렉트 URL을 지원하는 Controller를 만들 필요가 없다 스프링 시큐리티가 이미 구현해 놓았다.
  5. 클라이언트 ID와 클라이언트 보안 비밀코드를 프로젝트에서 확인

  6. application-oauth 파일 생성

    • 클라이언트 id와 보안비밀 코드를 등록

       

       

    • scope 별도 등록 이유 scope 기본값 (openid,profile,email)

    • openid라는 scope가 있으면 Open id Provider로 인식하기 때문이다.

    • 이렇게 되면 OpenId Provider인 서비스(구글)과 그렇지 않은 서비스(네이버/카카오등)로 나눠서 각각 OAuth2Service를 만들어야한다.

    • 하나의 OAuth2Service로 사용하기 위해 일부러 openid scope를 빼고 등록하는 것

  7. 스프링 부트에서는 properties의 이름을 application-xxx.properties로 만들면 xxx라는 이름의 profile이 생성되어 이를 통해 관리할 수 있다

    • application.properties에서 application-oauth.properties를 포함하도록 구성

    • application.properties에 다음과 같은 코드 추가

       

       

  8. .gitignore에 보안정보 노출안되도록 application-oauth.properties 추가

구글 로그인 연동하기

User 패키지 User , UserRepository , Role 클래스 생성

1. User

 

 

2. Role

 

 

  • 스프링 시큐리티에선 권한 코드에 항상 ROLE_이 앞에 있어야만 한다.
  • 그래서 코드별 키값을 ROLE_GUEST, ROLE, ROLE_USER 등으로 지정

3. UserRepositry

 

 

  • USER의 CRUD 담당 레파지토리
  • findByEmail
    • 소셜 로그인으로 반환되는 값 중 email을 통해 이미 생성된 사용자인지 아닌지 판단하기 위한 메소드

스프링 시큐리티 설정

1. 의존성 추가

  • compile('org.springframework.boot:spring-boot-starter-oauth2-client')
    • 소셜 로그인 등 클라이언트 입장에서 소셜기능 구현 시 필요한 의존성
    • spring-security-oauth2-client와 spring-security-oauth2-jose를 기본으로 관리 해준다

2. config.auth 패키지를 생성

  • 시큐리티 관련 클래스는 모두 이곳에 담는다.

3. SecurityConfig , CustomOAuth2UserService 생성

  • SecurityConfig

     

     

    • @EnableWebSecurity

      • Spring Security 설정들을 활성화
    • csrf().disable().headers().frameOption().disable()

      • h2-console 화면을 사용하기 위해 해당 옵션들을 disable 합니다

      • .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())

          .csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())로 .csrf를 방어하자
        CSRF란?
        • CSRF(Cross site request forgery)란 웹 사이트의 취약점을 이용하여 이용자가 의도하지 하지 않은 요청을 통한 공격을 의미합니다.
        • http 통신의 Stateless 특성을 이용하여 쿠키 정보만 이용해서 사용자가 의도하지 않은 다양한 공격들을 시도할 수 있습니다.
        • 해당 웹 사이트에 로그인한 상태로 https://xxxx.com/logout URL을 호출하게 유도하면 실제 사용자는 의도하지 않은 로그아웃을 요청하게 됩니다.
        • 실제로 로그아웃뿐만 아니라 다른 웹 호출도 가능하게 되기 때문에 보안상 위험합니다.
    • authorizeRequests

      • URL별 권한 관리를 설정하는 옵션의 시작점
      • 해당 메소드가 선언되어야지만 antMatchers 옵션을 사용할 수 있따.
    • antMatchers

      • 권한 관리 대상을 지정하는 옵션
      • URL,HTTP메소드별로 관리가 가능하다.
      • "/" 등 지정된 URL들은 permitAll() 옵션을 통해 전체 열람 권한을 주었다.
      • "/api/v1/**" 주소를 가진 API는 USER 권한을 가진 사람만 가능하도록 구현하였따
    • anyRequest

      • 설정된 값들 이외 나머지 URL들을 나타낸다
    • authenticated

      • 여기서 authenticated()를 추가하여 나머지 URL들은 모두 인증된 사용자들에게만 허용한다.
      • 여기서 인증된 사용자는 로그인한 사용자들이다
    • logout().logoutSuccessUrl("/")

      • 로그아웃 기능에 대한 여러 설정의 진입점
      • 로그아웃 성공시 / 주소로 이동한다.
    • oauth2Login()

      • OAuth2 로그인 기능에 대한 여러 설정의 진입점
    • userInfoEndpoint

      • OAuth2 로그인 성공 이후 사용자 정보를 가져올 때의 설정들을 담당한다.
    • userService(customOAuth2UserService)

      • 소셜 로그인 성공시 후속조치를 진행할 UserService 인터페이스의 구현체를 등록한다.
      • 리소스 서버 (즉 소셜서비스들)에서 사용자 정보를 가져온 상태에서 추가로 진행하고자 하는 기능을 명시 할 수 있습니다
  • CustomOAuth2UserService

    • 로그인 이후 가져온 사용자의 정보(email, name, picture등) 들을 기반으로 가입 및 정보수정, 세션 저장등의 기능을 지원한다.

       

       

    • String registrationId

      • 현재 로그인 진행 중인 서비스를 구분하는 코드
      • 로그인시 네이버로그인인지 구글 로그인인지 구분하기 위해서 사용한다.
    • String userNameAttributeName

      • OAuth2 로그인 진행 시 키가 되는 필드값. Primary Key와 같은 의미
      • 구글의 경우 기본적으로 코드를("sub") 지원하지만 네이버 카카오등은 기본적으로 지원하지 않는다.
      • 이 후 네이버 로그인과 구글 로그인 동시 지원할 때 사용
    • OAuthAttributes attributes

      • OAuth2UserService를 통해 가져온 OAuth2User의 attribute를 담을 클래스이다
      • 이후 네이버등 다른 소셜 로그인에서도 이클래스를 사용한다.
    • SessionUser(user)

      • 세선에 사용자 정보를 저장하기 위한 Dto 클래스
      • 왜 User 클래스를 쓰지 않고 새로 만들어서 쓰는지는 뒤이어서 상세히
    • private User saveOrUpdate(OAuthAttributes attributes) {}

      • 구글 사용자 정보가 업데이트 되었을 때를 대비하여 update 기능도 같이 구현
      • 사용자의 이름이나 프로필 사진이 변경되면 User 엔티티에도 반영된다.

OAuthAttributes 생성 (auth.dto)

 

 

  • OAuth2User에서 반환되는 사용자 정보는(attributes) map이기 때문에 값 하나하나를 반환해야만한다.
  • toEntity()
    • User 엔티티를 생성한다.
    • OAuthAttributes에서 엔티티를 생성하는 시점은 처음 가입할 때입니다.
    • 가입할 때의 기본권한을 GUEST로 주기위해서 role 빌더값에는 Role.GUEST를 사용합니다.
    • OAuthAttributes 클래스 생성이 끝났으면 같은 패키지에 SessionUser클래스를 생성한다.

SessionUser 생성

 

 

  • SessionUser에는 인증된 사용자 정보만 필요하다.
  • 그러니 name, email, picture만 필드로 선언한다.

User 클래스를 그대로 사용하면 안되는 이유

  • User 클래스를 그대로 사용했다면 다음과 같은 에러 발생

      Failed to convert from type [java.lang.Object] to type [byte[]] for value 'com.seongbinko.springboot.domain.user.User@4a43d6'
    • 세션에 저장하기 위해 User클래스를 직렬화를 구현하지 않았단 에러
    • User클래스가 entity이기 때문에 다른 엔티티와 관계가 생성될 지 모른다
    • 자식까지 직렬화 대상에 포함되다 보면은 성능 이슈, 부슈 효과가 발생할 확률이 높다
    • 그래서 직렬화 기능을 가진 세션 Dto를 하나 추가로 만드는 것이 유지보수에 도움이 된다.

로그인 테스트

 

 

  1. {{#userName}}
    • 머스테치는 다른 언어와 같은 if문 제공하지 않는다
    • true/false만 가능하다
    • 머스테치에서는 항상 최종값을 넘겨줘야한다.
    • userName이 있다면 노출하도록 구현
  2. a href="/logout"
    • 스프링 시큐리티에서 기본적으로 제공하는 로그아웃 url
    • 즉 개발자가 별도로 url에 해당하는 컨트롤러를 만들 필요가 없다
    • SecurityConfig 클래스에서 URL을 변경할 수 있지만 기본 url을 사용하여도 충분하다
  3. {{^userName}}
    • 머스테치에서는 해당 값이 존재하지 않는 경우 ^을 사용한다.
  4. a href="/oauth2/authorization/google"
    • 스프링 시큐리티에서 기본적으로 제공하는 로그인 URL
    • 로그아웃 url과 마찬가지로 개발자가 별도의 컨트롤러를 생성할 필요가 없다

index.mustache에서 userName을 사용할 수 잇게 IndexController에서 userName을 model에 저장하는 코드 추가

 

 

  • (SessionUser)httpSession.getAttribute("user")
    • 앞서 작성된 CustomOAuth2UserService에서 로그인 성공 시 세션에 SessionUser를 저장하도록 구성
    • httpSession.setAttribute("user", new SessionUser(user)); 인듯.!
    • 즉 로그인 성공시 httpSession getAttribute("user")에서 값을 가져올 수 있다.

어노테이션 기반으로 개선하기

  • indexController에서 세션값을 가져오는 부분

  • index 메소드 외에 다른 컨트롤러와 메소드에서 세션값이 필요하면 그 때마다 직접세션에서 값을 가져와야하는 문제

  • 메소드 인자로 세션값을 바로 받을 수 있도록 변경

  • LoginUser(annotation)

     

     

    • @Target(ElementType.PARAMETER)
      • 이 어노테이션이 생성될 수 있는 위치를 지정한다.
      • PARAMETER로 지정했으니 메소드의 파라미터로 선언된 객체에서만 사용할 수 있다
    • @interface
      • 이파일을 어노테이션 클래스로 지정한다.
      • LoginUser라는 이름을 가진 어노테이션이 생성되었다
    • @Retention(RetentionPolicy.RUNTIME)
      • 컴파일 이후에도 JVM에 의해서 참조가 가능합니다.
  • LoginUser와 같은 위치에 LoginUserArgumentResolver를 생성

     

     

    • HandlerMEthodArgumentResolver 인터페이슬 구현한 클래스
    • @Component
    • 조건에 맞는 경우 메소드가 있다면 HandlerMEthodArgumentResolver의 구현체가 지정한 값으로 해당 메소드의 파라미터를 넘길 수 있따
    • supportsParameter()
      • 컨트롤러 메서드의 특정 파라미터를 지원하는지 판단한다.
      • 여기서는 파라미터에 @LoginUser 어노테이션이 붙어있고, 파라미터 클래스타입이 SessionUser.class인 경우 true를 반환한다.
    • resolveArgument()
      • 파라미터에 전달할 객체를 생성한다
      • 여기서는 세션에서 객체를 가져온다

WebMvcConfigurer 추가

  • 이렇게 생성된 LoginUserArgumentResolver를 스프링에서 인식될 수 있도록 WebMvcConfigurer에 추가한다.

 

 

  • HandlerMethodArgumentResolver는 항상 WebConfigurer의 addArgumentResolvers()를 통해 추가해야한다.
  • 다른 HandlerMethodArgumentResolver 필요하다면 같은 방식으로 추가한다.
  • IndexController의 코드에서 반복되는 부분들을 모두 @LoginUser로 개선한다.

 

 

  • @LoginUser SessionUser user
    • 기존에 (SessionUser) httpSession.getAttribute("user")로 가져오던 세션 정보 값이 개선되었다
    • 이제는 어느 컨트롤러든지 @LoginUser만 사용하면 세션 정보를 가져올 수 있게 되었다

세션 저장소로 데이터베이스 사용하기

  • 현재 서비스는 애플리케이션을 실행하면 로그인이 풀린다.
  • 세션이 내장 톰캣의 메모리에 저장되어있기 때문
  • 배포할 때마다 톰캣이 재시작되기 때문
  • 톰캣을 세션 동기화 설정을 해야한다.

현업 세션 저장소 3가지

  1. 톰캣 세션을 사용
    • 일반적인 선택방식
    • 톰캣(was)에 세션이 저장되기 때문에 2대이상의 was가 구동되는 환경에서는 톰캣들 간의 세션 공유를 위해 추가 설정이 필요하다
  2. MySQL과 같은 데이터베이스를 세션 저장소로 사용한다.
    • 여러 was 간의 공용 세션을 사용할 수 잇는 가장 쉬운 방법
    • 설정이 간단하고 비용이 절감된다.
    • 다만 로그인 요청마다 DB IO가 발생하여 성능상 이슈 발생할 수 있다
    • 보통 로그인 요청이 많이 없는 백오피스, 사내 시스템 용도에서 사용한다.
  3. Redis, Memcached와 같은 메모리 DB를 세션 저장소로 사용한다.
    • B2C 서비스에서 가장 많이 사용되는 방식이다
    • 실제 서비스로 사용하기 위해서는 Embedded Redis 와 같은 방식이 아닌 외부 메모리 서버가 필요하다
  • 두번 째를 사용한다.

spring-session-jdbc 등록

  • build.gradle에 의존성 등록

     

     

  • application.properties에 세션 저장소를 jdbc로 선택하도록 코드를 추가

    • JPA로 인해 세션 테이블이 자동 생성되었기 때문에 별도로 해야할 일은 없다.
    • 세션 저장소를 데이터베이스로 톰캣(내장)에서 데이터베이스로 교체했다
    • but 스프링을 재시작하면 세션이 풀린다. 이유는 H2 기반으로 스프링이 재시작될 때 H2도 재시작 되기 때문
    • AWS의 데이터베이스 서비스인 RDS를 사용하면 세션이 풀리지 않게 된다.

네이버 로그인 등록

  • application-oauth.properties에 등록

     

     

    • user_name_attribute=response

      • 기준이 되는 user_name의 이름을 네이버에서는 response로 해야한다.
      • 네이버의 회원 조회 시 반환되는 JSON형태 때문이다.
    • 네이버 오픈 API의 로그인 회원결과

        {
            "resultcode": "00",
            "message": "success",
            "response": {
                "email": "openapi@naver.com".
                "nickname": OpenAPI",
                "profile_image": "https//ssl.pstatic.net/static/pwe/address/nodata_33x33/gif",
      
                "age": "40-49",
                "gender": "F",
                "id": "32742776",
                "name": "오픈API",
                "birthday": "10-01"
            }
        }
      • 스프링 시큐리티에선 하위 필드를 명시할 수 없다.
      • 최상위 필드들만 user_name으로 지정가능하다. 즉 resultcode, message,response만 가능하다
      • 그래서 response를 user_name으로 지정하고 이후 자바 코드로 response의 id를 user_name으로 지정하겠다
      스프링 시큐리티 설정 등록
    • OAuthAttributes
    • 아래와 같이 네이버인지 판단하는 코드와 네이버 생성자만 추가해주면 된다.

       

       

      index.mustahe에 네이버 로그인 추가
    • /oauth2/authorization/naver

      • 네이버 로그인 URL은 application-oauth.properties에 등록한 redirec-uri값에 맞춰 자동으로 등록된다. 여기선 registerationId
      • 여기서는 naver가 마지막 Path가 된다.
      • /oauth2/authorization/까지는 고정이고 마지막 Path만 각 소셜 로그인 코드를 사용하면 된다.

기존 테스트에 시큐리티 적용하기

기존 테스트에 시큐리티 적용으로 문제가 되는 부분 해결

  1. CustomOAuth2UserService를 찾을 수 없음

    • src/main 환경과 src/test 환경의 차이 때문

    • test에서는 application.properties가 test에 없으면 main의 설정을 가져오는데 딱 application.properties파일까지만 가져온다

    • test에 가짜 application.properties 추가하기

       

       

  2. 302 Status Code

    • 스프링 시큐리티 설정 때문에 인증되지 않은 사용자의 요청은 이동시키기 때문

    • API요청은 임의의 인증된 사용자를 추가하여 API만 테스트 해볼 수 있다.

    • 스프링 시큐리티 테스트를 위한 여러도구를 지원하는 spring-security-test를 build.gradle에 추가한다.

       

       

    • PostApiControllerTest 의 2개 테스트 메소드에 다음 과 같은 임의 사용자 인증을 추가한다.

      1. @WithMockUser(roles="USER")

        • 인증된 모의(가짜) 사용자를 만들어서 사용한다.

        • roles에 권한을 추가해 줄 수 있다.

        • 즉 어노테이션으로 인해 ROLE_USER 권한을 가진 사용자가 api를 요청하는 것과 동일한 효과

           

           

      2. MockMvc에서만 @WithMockUser 가 작동한다.

      3. @SpringBootTest에서 MockMvc를 사용하는 방법

        • import 부분 추가
        • @Before
          • 매번 테스트가 시작되기 전에 MockMvc인스턴스를 생성한다.
        • mvc.perform
          • 생성된 MockMvc를 통해 API를 테스트한다.
          • 본문(Body)영역은 문자열로 표현하기 위해 ObjectMapper를 통해 문자열 JSON으로 변환한다.
  3. @WebMvcTest에서 CustomOAut2UserService를 찾을 수 없음 (HelloControllerTest 미통과)

    • @WebMvcTest를 사용한다는 점

    • 1번을 통해 스프링 시큐리티 설정은 잘 동작 but @WebMvcTest는 CustomOAuth2UserService를 스캔하지 않기 때문

    • @WebMvcTest는 @ControllerAdvice, @Controller, WebSecurityConfigurerAdapter, WebMvcConfigurer 를 읽는다

    • @Repository, @Service, @Component는 스캔 대상이 아니다

    • HelloControllerTest

       

       

      • 스캔대상에서 SecurityConfig를 제거하고 @WithMockUser를 사용해서 가짜로 인증된 사용자를 생성한다.

      • 추가 에러발생

          java.lang.IllegalArgumnetException: At least one JPA metamodel must be present!
        • @EnableJpaAuditing로 인해 발생한다.

        • @EnableJpaAuditing를 사용하기 위해선 @Entity 클래스가 필요하다

        • @WebMvcTest이다 보니 당연히 없다

        • @EnableJpaAuditing가 @SpringBootApplication와 함께 있다보니 @WebMvcTest에서도 스캔하게 되었다

        • @EnableJpaAuditing와 @SpringBootApplication을 분리하자

           

           

        • config 패키지에 JpaConfig 생성 @EnableJpaAuditing를 추가

           

           

AWS 서버 환경 구축

  • AWS와 클라우드 서비스란?
  • AWS의 관리형 가상 서버인 EC2 서비스 소개와 생성 방법
  • EC2 인스턴스의 IP를 고정해주는 탄력적 IP에 대한 소개와 설정방법
  • EC2 인스턴스 접글을 위한 pem키 사용 방법
  • 리눅스 서버 생성 시 해야 할 설정들

AWS EC2

  • 클라우드 서비스란?
    • 인터넷(클라우드)을 통해 서버, 스토리지(파일저장소), 데이터베이스, 네트워크, 소프트웨어, 모니터링 등의 컴퓨팅 서비스를 제공하는 것
    • AWS의 EC2는 서버 장비를 대여하는 것이지만 실제로는 그안의 로그관리, 모니터링, 하드웨어 교체 테트워크 관리 등을 기본적으로 지원한다.
  • 클라우드 형태
    1. Infrastructure as a Service(IaaS, 아이아스)
      • 기존 물리 장비를 미들웨어와 함께 묶어둔 추상화 서비스
      • 가상머신, 스토리지, 네트워크, 운영체제 등의 IT 인프라를 대여해 주는 서비스
      • AWS EC2, S3 등
    2. Platform as a Service (PaaS, 파스)
      • 이에스에서 한 번 더 추상화한 서비스
      • 추상화 했기 때문에 많은 기능이 자동화 되어있다.
      • AWS의 Beanstalk(빈스톡) Heroku(헤로쿠) 등
    3. Software as a Service (SaaS, 사스)
      • 소프트웨어 서비스를 이야기한다.
      • 구글 드라이브, 드랍박스, 와탭 등
  • AWS를 선택한 이유 (AWS, Azure, GCP 등)
    1. 1년간 대부분 서비스가 무료
    2. 클라우드에서 기본적으로 지원하는 기능 (모니터링, 로그관리, 백업, 복구, 클러스터링 등등)이 많아 개발에 보다 집중
    3. AWS는 국내에서 활성화 되어있다
  • EC2를 사용하는 이유
    • 프리티어에서 빈스톡을 사용하면 무중단 배포가 불가능 하다.

EC2 인스턴스 생성하기

EC2(Elastic Compute Cloud) (서버)

  • AWS에서 제공하는 성능, 유량 등을 유동적으로 사용할 수 있는 서버
  • 리전을 서울로 변경
  • t2.micro만 사용하면 월750시간의 제한조건을 충족하여 무중단 배포를 할 수 있다 (1개만 생성 가능)
  • 크레딧(CPU를 사용할 수 있는 포인트)
    • 크레딧이 모두 사용되면 EC2를 사용할 수 없다.

인스턴스

  • EC2 서비스에 생성된 가상머신
  • AMI(Amazon Machine Image)
    • EC2 인스턴스를 시작하는데 필요한 정보를 이미지로 구워 논 곳
  • Amazon Linux AMI 1을 선택 및 이유
    • 국내 자료 多
    • 센토스 AMI보다 아마존이 개발하고 있기 때문에 AWS 각종 서비스와 상성이 좋다, 지원 받기 편하다
  • 보안 구룹
    • 방화벽 개념 (인바운드 규칙 추가로 접속 가능한 포트만 허용한다)
    • SSH & Port 22
      • AWS EC2에 터미널로 접속할 때 PEM키가 없으면 접속이 안된다.
      • PEM키 덕분에 전체오픈(0.0.0.0/0, ::/0) 하는 경우가 종종 있다
        • PEM키가 노출되면 끝나니 꼭 규칙을 추가해주자.
  • PEM키 생성
    • 인스턴스는 pem 이외의 접속을 허용하지 않는다.

EIP 할당

  • AWS의 고정IP, 탄력적 IP
    • 인스턴스도 서버이기 때문에 인스턴스 생성시 새 IP를 할당
    • IP를 매번 변경되지 않고 고정된 IP를 획득하기 위해 적용
    • 탄력적 IP는 생성하고 바로 할당 (비용청구 문제)

EC2 서버에 접속하기

  • Error 오랜시간 접속 안되거나, 권한이 없어서 안 된다는 메시지가 나온다면 다음을 꼭 확인하자
    1. HostName 값이 정확히 탄력적 ip로 되어있는지 확인
    2. EC2 인스턴스가 running 상태인지 확인
    3. EC2 인스턴스의 보안 그룹 → 인바운드 규칙에서 현재 본인의 IP가 등록되어 있는 지 확인
      • 인터넷 망 변경으로 내IP가 변동되어 접속되지 않던 문제 인바운드 추가로 해결

windows 용 putty 설치 (ssh 접속에 용이하게 하기 위해)

  • putty.exe

    • Host Name
      • ec2-user@탄력적 IP주소
    • Port
      • ssh 접속 포트인 22
  • puttygen.exe

    • pem키를 ppk파일로 변환하기 위한 클라이언트
  • 윈도우에서 EC2 접속 성공

아마존 리눅스1 서버 생성시 꼭 해야할 설정들 (자바 기반 웹 어플리케이션(톰캣, 스프링부트))

  1. java8 설치
  2. 타임존 변경
  3. 호스트 네임 변경
    • 현재 접속한 섭의 별명 등록
    • IP만으로는 어떤 서버가 어떤 역할을 하는지 알 수 없기 때문에 등록해야 한다.

AWS RDS (데이터 배이스 환경)

  • AWS의 관리형 데이터베이스 서비스인 RDS에 대한 소개와 생성방법
  • RDS로 서비스를 하는데 필요한 여러 파라미터 그룹 설정들
  • 인테리에지 커뮤니티 버전으로 데이터베이스를 다루는 방법
  • EC2와 RDS 간 연동 방법

RDS( Realational Database Service )

  • AWS에서 지원하는 클라우드 기반 관계형 데이터 베이스

RDS 인스턴스 MariaDB 선택 이유

  • 가격
  • Amazon Aurora(오로라) 교체 용의 성 (규모가 커진 서비스)

Maria DB 의 장점 (MySQL과 비교)

  1. MySQL의 Oracle 합병
  2. 동일 하드웨어 사양으로 MySQL보다 향상된 성능
  3. 좀 더 활성화된 커뮤니티
  4. 다양한 기능
  5. 다양한 스토리지 엔진

RDS 운영환경에 맞는 파라미터 설정하기

  • 타임존
  • Character Set
    • character항목은 utf8mb4, collation 항목은 utf8mb4_general_ci
    • mb4가 붙으면 이모지 저장이 가능하다.
  • Max Connection (150 설정)

내 PC에서 RDS에서 접속하기

보안그룹 인바운드 규칙에서 MYSQL/Aurora 선택 3306 포트

  • RDS의 보안 그룹에 본인 PC의 IP를 추가
  • EC2 보안 그룹을 추가한다. (vpc 보안 그룹을 복사)
    • EC2와 RDS 간에 접근이 가능하게 된다
    • EC2의 경우 이후에 2대 3대가 될 수 도 있는데 매번 IP를 등록할 수 는 없으니 보편적으로 이렇게 보안 그룹 간의 연동을 진행한다.

Database 플러그인 설치

  • 로컬에서 원격 데이터베이스로 붙을 때 GUI 클라이언트를 사용
  • Workbench, SQLyog, Sequel Pro(only Mac) DataGrip 등
  • 여기선 인텔리제이 데이터베이스 플러그인 사용한다.(무료)

EC2에서 RDS에서 접근 확인

  • mysql -u 계정 -p -h Host주소

EC2 서버에 프로젝트 배포

  • AWS EC2 서비스에 스프링 부트 프로젝트를 배포하는 방법
  • 간단한 쉘 스크립트 사용 방법
  • 스프링 부트 프로젝트와 AWS RDS 연동 방법
  • EC2에서 구글, 네이버 로그인 설정 방법

EC2에 프로젝트 Clone 받기

sudo yum install git

git -- version

---

mkdir ~/app && mkdir ~/app/step1 (디렉토리 생성)

cd ~/app/step (디렉토리 이동)

git clone 주소

코드 잘 수행되는지 테스트 검증

./gradlew test

테스트 실패시 수정하고 깃허브를 푸시했다면 다음 명령어 수행

git pull

다음과 같은 메시지가 뜬다면

-bash: ./gradlew: Permission denied 

chomod +x ./gradlew
  • EC2엔 그레이들을 설치하지 않았다. 하지만 Gradle Task(ex:test)를 수행 할 수 있다.
    프로젝트 내부에 포함된 gradlew 때문이다.
  • gradlew
    • 그레이들이 설치되지 않은 환경 혹은 버전이 다른 상황에서도 해당 프로젝트에 한해서 그레이들을 쓸 수 있도록 지원하는 Wrapper 파일이다

배포 스크립트 만들기

  • 작성한 코드를 실제 서버에 반영하는 것을 배포라고 한다.
    • git clone 혹은 git pull을 통해 새 버전의 프로젝트 받음
    • Gradle 이나 Maven을 통해 프로젝트 테스트와 빌드
    • EC2 서버에서 해당 프로젝트 실행 및 재실행

쉘 스크립트

  • 리눅스에서 기본적으로 사용할 수 있는 스크립트 파일
  • 배포할 때마다 개발자가 하나하나 명령어를 실행하는 것은 어려움
  • 쉘스크립트로 실행하여 일련의 과정이 차례로 진행되도록 하기 위함이다.

VIM

  • 리눅스 환경 처럼 GUI(윈도우와 같이 마우스를 사용할 수 있는 환경)가 아닌 환경에서 사용할 수 있는 편집도구

  • vim ~/app/step1/deploy.sh 생성

  • deploy.sh

      #!/bin/bash
    
      REPOSITORY=/home/ec2-user/app/step1
      PROJECT_NAME=seongbinko-springboot-webservice #1
    
      cd $REPOSITORY/$PROJECT_NAME/ #2
    
      echo "> Git pull"
      git pull #3
    
      echo "> 프로젝트 Build 시작"
      ./gradlew build #4
    
      echo "> step1 디렉토리로 이동"
    
      cd $REPOSITORY
    
      echo "> Build 파일복사"
    
      cp $REPOSITORY/$PROJECT_NAME/build/libs/*.jar $REPOSITORY/ #5
    
      echo "> 현재구동중인 앱pid 확인"
    
      CURRENT_PID=$(pgrep -f ${PROJECT_NAME}*.jar) #6
    
      echo ">현재구동중인 애플리케이션pid: $CURRENT_PID"
    
      if [ -z "$CURRENT_PID" ]; then   #7
              echo "> 현재구동중인 앱이 없으므로 종료하지 않습니다."
      else
              echo "> kill -15 $CURRENT_PID"
              kill -15 $CURRENT_PID
              sleep 5
      fi
    
      echo ">새 어플리케이션 배포"
    
      JAR_NAME=$(ls -tr $REPOSITORY/ | grep *.jar | tail -n 1)
    
      echo "> JAR Name: $JAR_NAME"
    
      nohup java -jar \  #9
               -Dspring.config.location=classpath:/application.properties,/home/ec2-user/app/application-oauth.properties,/home/ec2-user/app/application-real-db.properties \
               -Dspring.profiles.active=real \
               $REPOSITORY/$JAR_NAME  2>&1 &
    1. REPOSITORY=/home/ec2-user/app/step1

      • 프로젝트 디렉토리주소는 스크립트 내에서 자주 사용하는 값이기 때문에 이를 변수로 저장한다.
      • 마찬가지로 PROJECT_NAME=seongbinko-springboot-webservice도 동일하게 변수로 저장한다.
      • 쉘에서는 타입 없이 선언하여 저장한다.
      • 쉘에서는 $변수명으로 변수를 사용할 수 있다.
    2. cd $REPOSITORY/$PROJECT_NAME/

      • 제일 처음 git을 받았던 디렉토리로 이동
      • 바로 위의 쉘 변수 설명을 따라 /home/ec2-user/app/step1/seongbinko-springboot-webservice 주소로 이동한다.
    3. git pull

      • 디렉토리 이동후 , master 브랜치의 최신 내용을 받는다.
    4. ./gradlew build

      • 프로젝트 내부의 gradlew로 build를 수행한다.
    5. cp $REPOSITORY/$PROJECT_NAME/build/libs/*.jar $REPOSITORY/

      • build 결과물인 jar파일을 복사해 jar 파일을 해당 레파지토리 위치로 복사한다.
    6. CURRENT_PID=$(pgrep -f ${PROJECT_NAME}*.jar)

      • 기존에 수행 중이던 스프링 부트 애플리케이션의 PID를 변수에 저장한다. pgrep 명령어는 PID를 찾는 명령어이다.
      • pgrep은 process id만 추출하는 명령어
      • -f 옵션은 프로세스 이름으로 찾는다
    7. CURRENT_PID가 존재하면 기존에 실행중인 프로세스를 종료한다.

    8. JAR_NAME=$(ls -tr $REPOSITORY/ | grep *.jar | tail -n 1)

      • 새로 실행할 jar 파일명을 찾는다. tail -n 1은 여러 jar 파일 중에 가장 나중의(최신파일) jar 파일을 선택하게끔 해준다.
    9. nohup java -jar $/REPOSITORY/$JAR_NAME 2>&1 &

      • 찾은 jar 파일명으로 해당 jar 파일을 nohup으로 실행한다.

      • 스프링부트의 장점으로 특별히 외장 톰캣을 설치할 필요가 없다.

      • 내장 톰캣을 사용해서 jar파일만 있으면 바로 웹 어플리케이션 서버를 실행할 수 있다.

      • 일반적으로 자바가 실행할 때는 java -jar라는 명령어를 사용하지만, 이렇게 하면 사용자가 터미널 접속을 끊을때 어플리케이션도 같이 중지 되기때문에

        어플리케이션 실행자가 터미널을 종료해도 어플리케이션은 계속 구동될 수 있도록 nohup 명령어를 사용한다.

    10. chmod +x ./deploy.sh

      • 스크립트에 실행권한 부여
    11. ./deploy.sh 실행

    12. vim nohup.out 실행 오류 발생 (nohup out은 실행되는 모든 어플리케이션의 로그를 가지고 있다)

      • Consider 'org.springframework.wecurity.oauth2.client.registration.ClientRegistrationRepository' in your configuration
      ClientRegistrationRepository
    • 이를 생성하려면 clientId와 clientSecret가 필수

    • 로컬 PC에서는 application-oauth.properties가 있어서 문제가 없지만 깃허브에는 .gitignore로 git에서 제외대상 이라 깃허브에는 올라가 있지 않다.

    • 따라서 서버에서 직접 이 설정들을 가지고 있게 해야한다.

        Travis CI는 private github 저장소를 이용할 경우 요금이 부과되니 참고하여라
    • app 디렉토리에 properties 파일 생성 ( step1 뿐만 아니라 step2, step3도 쓰기 위함)

        vim /home/ec2-user/app/application-oauth.properties
      
        로컬에 있는 application-oauth.properties 를 그대로 복사한다. (네이버 로그인, 구글로그인 정보)
      
        spring.security.oauth2.client.registration.google.client-id=아이디
        spring.security.oauth2.client.registration.google.client-secret=비밀번호
        spring.security.oauth2.client.registration.google.scope=profile,email
      
        # registration
        spring.security.oauth2.client.registration.naver.client-id=
        spring.security.oauth2.client.registration.naver.client-secret=
        spring.security.oauth2.client.registration.naver.redirect-uri={baseUrl}/{action}/oauth2/code/{registrationId}
        spring.security.oauth2.client.registration.naver.authorization-grant-type=authorization_code
        spring.security.oauth2.client.registration.naver.scope=name,email,profile_image
        spring.security.oauth2.client.registration.naver.client-name=Naver
      
        # provider
        spring.security.oauth2.client.provider.naver.authorization-uri=https://nid.naver.com/oauth2.0/authorize
        spring.security.oauth2.client.provider.naver.token-uri=https://nid.naver.com/oauth2.0/token
        spring.security.oauth2.client.provider.naver.user-info-uri=https://openapi.naver.com/v1/nid/me
        spring.security.oauth2.client.provider.naver.user-name-attribute=response
    • deploy.sh가 application-oauth.properties을 쓰도록 deploy.sh 파일을 수정한다.

        nohup java -jar \  #9
                 -Dspring.config.location=classpath:/application.properties,/home/ec2-user/app/application-oauth.properties,/home/ec2-user/app/application-real-db.properties \ #1
                 -Dspring.profiles.active=real \ 
                 $REPOSITORY/$JAR_NAME  2>&1 &
      • Dspring.config.location
        • 스프링 설정 파일 위치를 지정한다.
        • 기본 옵션들을 담고 있는 application.properties와 OAuth 설정들을 담고 있는 application-oauth.properties, application-real-db.properties 위치를 지정
        • classpath가 붙으면 jar 안에 있는 resources 디렉토리를 기준으로 경로가 생성된다.
        • application-oauth.properties 와 application-real-db.properties 은 절대경로를 사용한다. 외부에 파일이 있기 때문이다.
    • ./deploy.sh 실행 (정상 확인)

스프링 부트 프로젝트로 RDS 접근하기

RDS MariaDB에서 스프링부트 프로젝트를 실행하기 위해 필요한 작업

  1. 테이블생성
    • H2에서 자동 생성해주던 테이블들을 MariaDb에선 직접 쿼리를 이용하여 생성한다.
  2. 프로젝트 설정
    • 자바 프로젝트가 MariaDB에 접근하려면 데이터베이스 드라이버가 필요
    • MariaDB에서 사용 가능한 드라이버를 프로젝트에 추가한다.
  3. EC2 (리눅스 서버) 설정
    • DB의 접속 정보는 중요하게 보호해야 할 정보
    • 프로젝트 안에 접속 정보를 갖고 있다면 깃허브와 같이 오픈된 공간에서는 해킹의 위험이 있다
    • EC2 서버 내부에서 접속정보를 관리하도록 설정한다.

1. RDS 테이블 생성

  • JPA가 사용할 엔티티 테이블, SpringSession이 사용할 테이블

    • 테스트 코드 수행시 로그로 생성되는 쿼리를 사용한다.

    • schema-mysql.sql 파일에서 로그 확인

      RDS에 반영

        use seongbinko_webservice;
      
        CREATE TABLE SPRING_SESSION (
            PRIMARY_ID CHAR(36) NOT NULL,
            SESSION_ID CHAR(36) NOT NULL,
            CREATION_TIME BIGINT NOT NULL,
            LAST_ACCESS_TIME BIGINT NOT NULL,
            MAX_INACTIVE_INTERVAL INT NOT NULL,
            EXPIRY_TIME BIGINT NOT NULL,
            PRINCIPAL_NAME VARCHAR(100),
            CONSTRAINT SPRING_SESSION_PK PRIMARY KEY (PRIMARY_ID)
        ) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;
      
        CREATE UNIQUE INDEX SPRING_SESSION_IX1 ON SPRING_SESSION (SESSION_ID);
        CREATE INDEX SPRING_SESSION_IX2 ON SPRING_SESSION (EXPIRY_TIME);
        CREATE INDEX SPRING_SESSION_IX3 ON SPRING_SESSION (PRINCIPAL_NAME);
      
        CREATE TABLE SPRING_SESSION_ATTRIBUTES (
            SESSION_PRIMARY_ID CHAR(36) NOT NULL,
            ATTRIBUTE_NAME VARCHAR(200) NOT NULL,
            ATTRIBUTE_BYTES BLOB NOT NULL,
            CONSTRAINT SPRING_SESSION_ATTRIBUTES_PK PRIMARY KEY (SESSION_PRIMARY_ID, ATTRIBUTE_NAME),
            CONSTRAINT SPRING_SESSION_ATTRIBUTES_FK FOREIGN KEY (SESSION_PRIMARY_ID) REFERENCES SPRING_SESSION(PRIMARY_ID) ON DELETE CASCADE
        ) ENGINE=InnoDB ROW_FORMAT=DYNAMIC;

2. 프로젝트 설정

  1. MariaDB 드라이버를 build.gradle에 등록

     compile('org.mariadb.jdbc:mariadb-java-client')
  2. 서버에서 구동할 환경인 스프링 profile를 구성한다. (rds 접속정보)

    • application-real.properties 파일 추가

      • 이 이름으로 파일을 만들면 profile=real인 환경이 구성된다.

      • 실제 운영될 환경이기 때문에 보안/로그상 이슈가 될만 설정들을 모두 제거하며 RDS환경 profile 설정이 추가된다.

      • 깃 허브로 푸시

         

         

3. EC2 설정

  • OAuth와 함께 RDS 접속 정보도 보호해야 할 정보이니 EC2 서버에 직접 설정 파일을 둔다.

  • app 디렉토리에 application-real-db.properties 파일을 생성

      vim ~/app/application-real-db.properties
      spring.jpa.hibernate.ddl-auto=none
      spring.datasource.url=jdbc:mariadb://seongbinko-webservice.cqhe5x38bxez.ap-northeast-2.rds.amazonaws.com:3306/seongbinko_webservice
      spring.datasource.username=seongbinko
      spring.datasource.password=dndiehsdk93!
      spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
    • spring.jpa.hibernate.dll-auto=none
      • jpa로 테이블이 자동 생성되는 옵션을 None으로 지정한다.
      • RDS에는 실제 운영으로 사용될 테이블이니 절대 스프링 부트에서 새로 만들지 않도록 해야한다.
      • 이 옵션을 하지 않으면 자칫 테이블이 모두 새로 생성될 수 있다
  • deploy.sh가 real profile을 쓸수 있도록 수정한다.

      nohup java -jar \  
               -Dspring.config.location=classpath:/application.properties,/home/ec2-user/app/application-oauth.properties,/home/ec2-user/app/application-real-db.properties \
               -Dspring.profiles.active=real \ #1
               $REPOSITORY/$JAR_NAME  2>&1 &
    • application-real.properties를 활성화 시킨다.

    • application-real.properties의 spring.profiles.include=oauth,real-db 옵션 때문에 real-db 역시 함께 활성화 대상에 포함된다.

       

       

    • ./deploy.sh 후 vim nohup.out을 통해 다음 로그 확인

        Tomcat started on port(s): 8080 (http) with context path ''
        Started Application in ~~ seconds (JVM running for ~~)
    • curl localhost:8080 으로 html 코드가 정상적으로 보인다면 성공!

EC2에서 소셜 로그인하기

1. AWS 보안 그룹 변경

  • springboot가 8080에 열려있으니 8080포트가 열려있는지 확인한다.

2. AWS EC2 도메인으로 접속

  • 퍼블릭 DNS가 EC2에 자동으로 할당된 도메인

  • 이 주소를 입력하면 우리의 EC2서버에 어디서나 접근 가능

  • 도메인 주소에 8080포트를 붙여 접속

    스프링부트 웹서비스

3. 구글에 EC2 주소 등록

Please sign in

4. 네이버에 EC2 주소 등록

  • 서비스 URL과 Callback URL 수정한다.
  1. 서비스 URL

    • 로그인을 시도하는 서비스가 네이버에 등록된 서비스인지 판단하기 위한 항목
    • 8080포트는 제외하고 실제 도메인 주소만 입력한다.
    • 네이버에서 아직 지원하지 않아 하나만 등록 간으하다.
    • 즉 EC2의 주소를 등록하면 localhost는 되지 않는다.
    • 개발 단계에서는 등록하지 않는 것을 추천한다.
    • localhost도 테스트하고 싶으면 네이버 서비스를 하나 더 생성해서 키를 발급받는다.
  2. Callback URL

    Please sign in

5. 현재까지의 문제점

  1. 수동 실행되는 TEST
    • 내가 짠 코드가 다른 개발자의 코드에 영향을 끼치지 않는지 확인하기 위해 전체 테스트를 수행해야만 한다. (Gradle에서의 test)
    • 현재 상태에선 항상 개발자가 작업을 진행할 때마다 수동으로 전체 테스트를 수행해야 한다.
  2. 수동 Build
    • 다른 사람이 작성한 브랜치와 본인이 작성한 브랜치가 합쳐졌을때(Merge) 이상이 없는지는 Build를 수행해야만 알 수 있다.
    • 이를 매번 개발자가 직접 실행해봐야만 한다.
  • 개선안
    • 깃허브에 푸시를 하면 자동으로 Test & Build & Deploy가 진행하도록 개선한다.

Travis CI 배포 자동화 (코드 푸시시 자동으로 배포)

  • CI/CD 란
  • 깃허브의 무료 CI 서비스인 Travis CI에 대한 소개와 프로젝트 연동 방법
  • AWS의 CD 서비스인 CodeDEploy에 대한 소개화 프로젝트 연동 방법
  • 수동 배포 방식에서 자동화 방식으로의 개선
  • CodeDeploy에서 오류로그를 보는 방법

CI (Continuous Integration- 지속적 통합) & CD (Continuous Deployment - 지속적인 배포)

CI

  • VCS 시스템 (Git, SVN 등)에 PUSH가 되면 자동으로 테스트와 빌드가 수행되어 안정적인 배포 파일을 만드는 과정
  • CI에 대한 4가지 규칙(마틴 파울러)
    1. 모든 소스코드가 살아있고(현재 실행되고) 누구든 현재의 소스에 접근할 수 있는 단일 지점을 유지할 것
    2. 빌드 프로세스를 자동화해서 누구든 소스로부터 시스템을 빌드하는 단일 명령어를 사용할 수 있게 할 것
    3. 테스팅을 자동화해서 단일 명령어로 언제든지 시스템에 대한 건전한 테스트 수트를 실행할 수 있게 할 것
    4. 누구나 현재 실행파일을 얻으면 지금까지 가장 완전한 실행 파일을 얻엇다는 확신을 하게 할 것

CD

  • 빌드 결과를 자동으로 운영 서버에 무중단 배포까지 진행되는 과정

Travis CI 연동하기

  • Travis CI는 깃허브에서 제공하는 무료 CI 서비스 (젠킨스는 설치형 이기 때문에 한단계를 더 거쳐야한다)
  • Travis 가입 후 Settings에서 사용한 프로젝트 활성화

프로젝트 설정

  • Travis CI의 상세한 설정은 .travis.yml파일로 할 수 있다

YAML(야믈)

  • .yml 파일 확장자를 야믈이라 한다.
  • JSON에서 괄호를 제거한 것이다.
  • 기계에서 파싱하기 쉽게 사람들이 다루기 쉽게가 이념이다.
  • build.gradle과 같은 위치에서 travis.yml을 생성한 후 다음의 코드를 추가한다.
  • travis.yml

 

 

 

  1. branches
    • Travis CI를 어느 브랜치가 푸시될 때 수행할지 지정한다
    • 재 옵션은 오직 master 브랜치에서 push될 때만 수행한다.
  2. cache
    • 그레이들을 통해 의존성을 받게되면 이를 해당 디렉토리에 캐시하여 같은 의존성은 다음 배포 때부터 다시 받지 않도록 설정한다.
  3. script
    • master브랜치에 푸시되었을 때 수행하는 명령어다.
    • 프로젝트 내부에 둔 gradlew를 통해 clean & build를 수행한다.
  4. notifications
    • Tavis CI 실행 완료 시 자동으로 알람이 가도록 설정한다.\
  • push 후 Travis에서 passed 확인 및 메일 확인 (build 정상인지 확인)

Travis CI와 AWS S3 연동하기

  • S3(Simple Storage Service)

    • AWS에서 제공하는 일종의 파일 서버
    • 이미지 파일을 비롯한 정적 파일들을 관리하거나 배포 파일들을 관리하는 등의 기능을 지원한다.
    • 첨부파일 , 이미지 업로드 구현시 S3를 이용하여 구현한다.
    • CodeDeploy는 저장 기능이 없기 때문에 Travis CI가 빌드한 결과물을 받아서 CodeDeploy가 가져갈 수 있도록 보관 할 수 있는 공간으로 사용한다.
  • Travis CI 연동시 구조

    1. Travis CI와 S3를 연동한다.
    2. 실제 배포는 AWS CodeDeploy라는 서비스를 이용한다.
    3. Jar 파일을 전달하기 위해서는 S3 연동이 먼저 필요하다
    4. S3와 CodeDeploy를 통해 빌드와 배포 기능을 분리한다.

     

AWS Key 발급 (IAM 서비스)

  • AWS 서비스에 외부서비스를 접근하기 위해서는 접근 가능한 권한을 가진 Key를 생성하여 사용하여야 한다.
  • IAM을 통해 Travis CI가 AWS의 S3와 CodeDeploy에 접근할 수 있도록 한다.
  1. IAM 검색 후 사용자 추가
  2. 사용자 이름과, 엑세스 유형은 프로그래밍 방식 엑세스, 기존 정책 직접연결 선택
  3. s3full, CodeDeployFull을 검색하여 체크
  4. 엑세스 키 ID와 비밀 엑세스 키 생성 완료
    • Travis CI에서 사용될 키

Travis CI에 키 등록

  • Settings → Environment Variables
    • AWS_ACCESS_KEY, AWS_SECRET_KEY를 변수로 해서 IAM 키 값들을 등록한다.
  • 등록 된 값은 .travis.yml에서 $AWS_ACCESS_KEY, $AWS_SECRET_KEY 이란 이름으로 사용할 수 있다.

S3버킷 생성 (위 키를 이용해서 Jar를 관리)

  • 파일 서버 역할
  • Travis CI에서 생성된 Build파일을 저장하도록 구성
  • S3에 저장된 Build 파일은 이후 AWS의 CodeDeploy에서 배포할 파일로 가져가도록 구성할 것이다.
  1. S3 검색 후 버킷명 작성
    • 배포할 Zip파일이 모여있는 장소
    • seongbinko-springboot-build
  2. 버전관리 (pass)
  3. 버킷의 보안과 권한 설정
    • 모든 퍼블릭 엑세스 차단
    • 현재 프로젝트는 이미 깃허브에 오픈소스로 풀려있으니 문제 없지만 실제 서비스에서 할 때는 Jar 파일이 퍼블릭일 경우 누구나 내려받을 수 있어 코드나 설정값, 주요 키값들이 모두 탈취될 수 있다.
    • 퍼블릭이 아니더라도 IAM 사용자로 발급받은 키를 사용하여 우리는 접근 가능하다.
  4. S3로 배포파일을 전달

.travis.yml 코드추가(S3로 배포파일을 전달)

 

 

  1. before_deploy
  • deploy 명령어가 실행되기 전해 수행된다.
  • CodeDeploy는 Jar파일을 인식하지 못하므로 Jar+기타 설정 파일들을 모아 압축(zip) 합니다.
  1. zip -r before-deploy *
  • before-deploy를 전체 압축
  1. mkdir -p deploy
  • deploy라는 디렉토리를 생성한다.
  1. mv before-deploy/before-deploy.zip deploy/seongbinko-springboot-webservice.zip
  • 해당 파일을 deploy 디렉토리로 다음과 같은 이름으로 변경하여 이동시킨다.
  1. deploy:
  • S3로 파일 업로드 혹은 CodeDeploy로 배포 등 외부 서비스와 연동될 행위들을 선언한다.
  1. local_dir
  • 앞에서 생성한 deploy 디렉토리를 지정한다.
  • 해당 위치의 파일들만 S3로 전달한다.
  • github 푸시

Travis CI와 AWS S3, CodeDeploy 연동하기

  • 배포대상인 EC2가 CodeDeploy를 연동 받을 수 있게 IAM 역할을 하나 생성

EC2에 IAM 역할 추가하기 (EC2 → codedeploy)

  • IAM → 역할 → 역할 만들기
  • AWS 서비스, EC2 선택
  • EC2RolerForA 검색하여 AmazonEC2RoleforAWSCodeDeploy 선택
  • ec2-codedeploy-role 이름으로 생성

IAM 사용자와 역할의 차이

 

IAM 사용자와 역할의 차이

A new tool for teams & individuals that blends everyday work apps into one.

www.notion.so

역할을 EC2 서비스에 등록

  • EC2 작업 → 인스턴스 설정 → IAM역할 연결/바꾸기 (ec2-codedeploy-role ) → 재부팅(정상적용)
  • CodeDeploy의 요청을 받을 수 있게 에이전트 설치

CodeDeploy 에이전트 설치

  • Putty로 EC2에 접속해서 다음 명령어를 입력한다.

      aws s3 cp s3://aws-codedeploy-ap-northeast-2/latest/install .
      --region ap-northeast-2
    
      #다음 메시지가 콘솔에 출력
      download: s3://aws-codedeploy-ap-northeast-2/latest/install
      to ./install
    
      #install 파일에 실행권한 추가
      chmod +x ./install
    
      #install 파일로 설치 진행
      sudo ./install auto
    
      #설치 완료후 Agent가 정상 실행되는지 상태검사
      sudo service codedeploy-agen status
    
      #running 메시지가 출력되면 정상
      The AWS CodeDeploy agent is running as PID xxx
    
      #만약 설치 중에 다음과 같은 에러가 발생한다면 루비 언어가 설치 안된 상태라서 그렇다.
      /user/bin/env: ruby: No such file or directory
      #이럴 경우 yum install로 루비를 설치해 준다.
      sudo yum install ruby

CodeDeploy를 위한 권한 생성 (codedeploy → EC2)

  • CodeDeploy에서 EC2에서 접근하려면 권한이 필요하다.
  • AWS서비스 → CodeDeploy 선택
  • codedeploy-role 역할 이름으로 생성

CodeDeploy 생성및 설정

AWS 배포 3형제

  1. Code Commit (Git hub)
    • 깃허브와 같은 코드 저장소의 역할
    • private 기능을 지원하지만 github에서 무료로 지원하고 있어서 거의 사용 x
  2. Code Build (Travis CI)
    • Travis CI와 마찬가지로 빌드용 서비스
    • 멀티모듈을 배포해야 하는 경우 사용해 볼만하지만, 규모가 있는 서비스에서는 대부분 젠킨스/팀시티 이용
  3. CodeDeploy
    • AWS배포 서비스
    • 대체재가 없다
    • 오토 스케일링 그룹 배포, 블루 그린 배포, 롤링 배포, EC2 단독 배포등 많은 기능을 지원한다.

  1. 배포 그룹생성
    • 배포그룹 이름 : seongbinko-springboot-webservice-group
    • 서비스 역할 : codedeploy-role
    • 배포 유형 : 현재 위치
      • 만약 본인이 배포할 서비스가 2대 이상이라면 블루/그린을 선택
        여기선 1대의 EC2만 배포하므로 선택하지 안는다.
    • 환경구성 : Amazon EC2 인스턴스
      • Name : ko-springboot-webservice
    • 배포구성
      • CodeDeployDefault.AllAtOnce
      • 한번 배포할 때 몇 대의 서버에 배포할지를 결정한다.
      • 2대 이상이라면 1대씩 배포할지, 30% 혹은 50%로 나눠서 배포할지 등등
      • 1대 서버이니까 전체를 배포하는 옵션으로 선택
    • 로드밸런싱 해제

Travis CI와 AWS S3, CodeDeploy 연동

  • S3에서 넘겨줄 zip 파일을 저장할 디렉토리 생성

      mkdir ~/app/step2 && mkdir ~/app/step2/zip
  • Travis CI의 Build가 끝나면 S3에 zip 파일이 전송되고, 이 zip 파일은 /home/ec2-user/app/step2/zip로 복사되어 압축을 풀 예정

  • Travis CI 설정은 .travis.yml

      해당 부분 추가
      -provider: codedeploy
          access_key_id: $AWS_ACCESS_KEY # Travis repo settings에 설정된 값
          secret_access_key: $AWS_SECRET_KEY # Travis repo settings에 설정된 값
          bucket: seongbinko-springboot-build # S3 버킷
          key: seongbinko-springboot-webservice.zip # 빌드 파일을 압축해서 전달
          bundle_type: zip
          application: seongbinko-springboot-webservice # 웹 콘솔에서 등록한 CodeDeploy 어플리케이션
          deployment_group: seongbinko-springboot-webservice-group # 웹콘솔에서 등록한 CodeDeploy 배포 그룹
          region: ap-northeast-2
          wait-until-deployed: true
  • AWS CodeDeploy 설정은 appspec.yml

     

     

    1. version:0.0
      • CodeDeploy 버전
      • 프로젝트 버전이 아니므로 0.0 외에 다른 버전을 사용하면 오류가 발생
    2. source
      • CodeDeploy에서 전달해 준 파일 중 destination으로 이동시킬 대상을 지정한다.
      • 르트 경로(/)를 지정하면 전체 파일을 이야기한다.
    3. destination
      • source에서 지정된 파일을 받을 위치 입니다
      • 이후 Jar를 실행하는 등은 destination에서 옮긴 파일들로 진행된다.
    4. overwrite
      • 기존에 파일들이 있으면 덮어쓸지를 결정
    • commit push, Travis CI가 끝나면 CodeDeploy 화면 아래에서 배포가 수행된 것을 볼 수 있다

배포 자동화 구성

  • Travis CI와 AWS S3, CodeDeploy 연동 구현
  • Jar를 배포하여 실행까지 해보자

deploy.sh 파일추가 (step2 환경)

 

 

  1. CURRENT_PID
    • 현재 수행중인 스프링부트 애플리케이션의 프로세스 ID를 찾습니다
    • 실행 중이면 종료하기 위해서입니다.
    • 스프링 부트 애플리케이션 이름으로 된 다른 프로그램이 있을 수 있어 seongbinko-springboot-webservice로 된 jar(pgrep -fl seongbinko-springboot-webservice | grep jar) 프로세스를 찾은 뒤 ID를 찾는다 (| awk '{print $1}')
  2. chomod + x $JAR_NAME
    • Jar 파일은 실행 권한이 없는 상태
    • nohup으로 실행할 수 있게 실행 권한을 부여한다.
  3. $JAR_NAME > $REPOSITORY/nohup.out 2>&1 &
    • nohup 실행 시 CodeDeploy는 무한 대기합니다
    • 이 이슈를 해결하기 위해 nohup.out 파일을 표준 입출력용으로 별도로 사용합니다.
    • 이렇게 하지 않으면 nohup.out 파일이 생기지 않고 , CodeDeploy 로그에 표준 입출력이 출력이 출력 됩니다.
    • nohup이 끝나기 전까지 CodeDeploy도 끝나지 않으니 꼭 이렇게 해야한다.
  • step1에서 작성된 deploy.sh와의 차이는 git pull을 통해 직접 빌드 했떤 부분을 제거
  • Jar 실행 단계에서 몇가지 코드 추가

.travis.yml 파일 수정

  • 현재는 프로젝트의 모든 파일을 zip 파일로 만드는데, 실제로 필요한 파일들은 Jar, appspec.yml,배포를 위한 스크립트 뿐이다.

  • 이외 나머지는 배포에 필요하지 않으니 포함 하지 않는다

  • .travis.yml 파일의 before_deploy를 수정한다.

     

     

    1. mkdir -p before-deploy
      • Travis CI는 S3로 특정 파일만 업로드가 안된다.
      • 디렉토리 단위로만 업로드할 수 있기 때문에 deploy 디렉토리는 항상 생성한다.
    2. before-deploy에는 zip파일에 포함시킬 파일들을 저장한다.
    3. zip-r 명령어를 통해 before-deploy 디렉토리 전체 파일을 압축한다.

appspec.yml 파일 수정

 

 

  1. permissions

    • CodeDeploy에서 EC2 서버로 넘겨준 파일들이 모두 ec2-user 권한을 갖도록 한다.
  2. hooks

    • CodeDeploy 배포 단계에서 실행할 명령어를 지정한다.

    • ApplicationStart 단계에서 deploy.sh를 ec2-user 권한으로 실행하게 한다.

    • timeout: 60으로 스크립트 실행 60초 이상 수행되면 실패가 됩니다(무한정 기다릴 수 없으니 시간제한을 둬야만 한다.)

    • 현재코드

        hooks:
      
            ApplicationStart:
                - location: deploy.sh
                    timeout: 60
                    runas: ec2-user

실제 배포 과정체험

  • build.gradle에서 프로젝트 버전을 변경 한다. (버전이 변경할 때 snapshot 버전도 바꿔준다)

      version '1.0.1-SNAPSHOT

CodeDeploy 로그 확인 (오류)

  • CodeDeploy와 같이 AWS가 지원하는 서비스에서 오류가 발생했을 때 로그 찾는 방법을 모른다면 오류를 해결하기가 어렵다.
  • 배포가 실패하면 어느 로그를 봐야 할지 간단하게 알려준다
  • CodeDeploy에 관한 대부분 내용은 /opt/codedeploy-agent/deployment-root에 있다
  • 해당 목록을 확인해보면 (ll) 다음과 같은 내용을 확인 할 수 있다
  • /opt/codedeploy-agent/deployment-root
drwxr-xr-x 7 root root 4096 Mar 29 13:18 8293e20b-491e-411d-bf71-ca61e33ff4a4 @1
drwxr-xr-x 2 root root 4096 Mar 29 13:18 deployment-instructions
drwxr-xr-x 2 root root 4096 Mar 15 18:45 deployment-logs                      @2
drwxr-xr-x 2 root root 4096 Mar 29 13:19 ongoing-deployment                   
  1. 최상단의 영문과 대시(-)가 있는 디렉토리명은 CodeDeploy ID 입니다
    • 사용자마다 고유한 ID가 생성되어 각자 다른 ID가 발급된다
    • 해당 디렉토리로 들어가보면 배포한 단위별로 배포 파일들이 있다.
    • 본인의 배포 파일이 정상적으로 왔는지 확인해 볼 수 있다.
  2. CodeDeploy 로그 파일이다
    • CodeDeploy로 이루어지는 배포 내용 중 표준 입/출력 내용은 모두 여기에 담겨 있다
    • 작성한 echo 내용도 모두 표기된다.

EC2에 자동으로 배포 되었지만 남은 문제점

  • 배포하는 동안 스프링 부트 프로젝트는 종료 상태가 되어 서비스를 이용할 수 없다.
  • 긴 기간은 아니지만 새로운 jar가 실행되기 전까진 기존 jar를 종료시켜 놓기 때문에 서비스가 중단된다.
  • 배포하는 동안 서비스 중단 없는 배포 방법 소개

24/7 중단 없는 서비스

  • 무중단 배포 소개
  • 웹 서버이자 로드밸런서 역할을 하는 엔진엑스
  • 엔진엑스를 이용한 무중단 배포 방법
  • source 명령어를 이용한 쉘 스크립트 파일 import 방법

무중단 배포 방식

  1. AWS에서 블루 그린 무중단 배포
  2. 도커를 이용한 웹서비스 무중단 배포
  3. L4 스위치를 이용한 무중단 배포 ( 고가)
  4. 엔진엑스를 이용한다.

엔진엑스(NginX)를 이용한 무중단 배포

  • 엔진엑스
    • 웹 서버, 리버스 프록시, 캐싱, 로드밸런싱, 미디어 스트리밍 등을 위한 오픈소스 소프트웨어
    • 아파치가 대세였던 자리를 빼앗은 웹서버이자 오픈소스
    • 리버스 프록시
      • 엔진엑스가 외부의 요청을 받아 백앤드 서버로 요청을 전달하는 행위
    • 리버스 프록시 서버(엔진엑스)는 요청을 전달하고 요청에 대한 처리는 뒷단의 웹 어플리케이션 서버가 처리한다.

구조

    • 엔진엑스는 80(http), 443(https) 포트를 할당한다.
    • 스프링부트1은 8081포트로 실행한다.
    • 스프링부트2는 8082포트로 실행한다.
    • 하나의 EC2 혹은 리눅스 서버에 엔진엑스 1대와 스프링 부트 jar를 2대를 사용하는 것

  1. 엔진엑스 무중단 배포1 구조

    1. 사용자는 서비스 주소로 접속한다(80포트 혹은 443 포트)
    2. 엔진엑스는 사용자의 요청을 받아 연결된 스프링 부트로 요청을 전달한다.
      • ex 8081포트로 요청을 전달한다고 가정
    3. 스프링부트2는 엔진엑스와 연결된 상태가 아니니 요청받지 못한다.
    4. 1.1 버전으로 신규배포가 필요하면 엔진엑스와 연결되지 않은 스프링 부트2(8082)로 배포한다.

     

  2. 엔진엑스 무중단 배포2


    1. 배포하는 동안에도 서비스는 중단되지 않는다.
      • 엔진엑스는 스프링부트1을 바라보기 때문
    2. 배포가 끝나고 정상적으로 스프링부트2가 구동중인지 확인
    3. 스프링부트2가 정상 구동 중이면 nginx reload 명령어를 통해 8081 대신에 8082를 바라보도록 한다.
    4. nginx reload는 0.1초 이내에 완려된다.

엔진엑스 무중단 배포3

  1. 2와 마찬가지로 수행

무중단 배포 전체 구조

엔진엑스 설치와 스프링부트와 연동

  1. 엔진엑스 설치

     - 엔진엑스 설치
     sudo yum install nginx
    
     - 실행
     sudo service nginx start
    
     - 메시지 확인
     Starting nginx: [ok]
  2. 보안그룹 추가

3. 리다이렉션 주소추가

 

Feedback Service

 

ec2-3-34-29-36.ap-northeast-2.compute.amazonaws.com

스프링 부트와 연동

  • 엔진엑스가 현재 실행 중인 스프링 부트 프로젝트를 바라볼 수 있도록 프록시 설정

      sudo vim /etc/nginx/nginx.conf
    
      - server 아래 location / 부분을 찾아서 다음과 같이 추가
    
      proxy_pass http://localhost:8080         #1 ?? 이해가 안되네
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; #2
      proxy_set_header Host $http_host;
    
      -엔진엑스 재시작
      sudo service nginx restart
    1. proxy_pass

    2. proxy_set_header

      • 실제 요청 데이터를 header의 각 항목에 할당한다.

      • 예) proxy_set_header X-Real-IP $remote_addr;

        Request Header의 X-Real-IP에 요청자의 ip를 저장한다

무중단 배포 스크립트 만들기

  • 무중단 배포 스크립트 작성 전에 API 추가 이 API는 배포 시에 8081을 쓸지 8082를 쓸지 판단하는 기준이된다.

profile API 추가

  • ProfileController.java

     

     

    1. env.getActiveProfiles()
      • 현재 실행 중인 ActiveProfile을 모두 가져온다
      • 즉 real, oauth, real-db 등이 활성화 되어 있다면(active) 3개가 모두 담겨 있다
      • 여기서 real, real1, real2는 모두 배포에 사용될 profile이라 이 중 하나라도 있으면 그 값을 반환하도록 한다.
      • 실제로 이번 무중단 배포에서는 real1과 real2만 사용되지만 step2를 다시 사용해 볼 수 도 있으니 real도 남겨둔다.
  • 테스트 코드 작성 ( 특별히 스프링 환경이 필요하지 않아 @SpringBootTest 없이 진행 한다.)

  • ProfileControllerUnitTest.java

     

     

    • ProfileController와 Environment 모두 자바클래스(인터페이스) 이기 때문에 쉽게 테스트 가능하다
    • Environment는 인터페이스라 가짜 구현체인 MockEnvironment(스프링에서 제공)를 사용하여 테스트 하면 된다.
    • DI의 유용성을 알 수 있다
    • Environment를 @Autowired로 DI 받았따면 이런 테스트 코드는 작성이 불가했다.
  • /profile이 인증 없이도 호출 될 수 있게 SecurityConfig 클래스에 제외 코드를 추가한다.

      .antMatchers("/", "/css/**", "/images/**", "/js/**", "/h2-console/**", "/profile").permitAll()
    
      "/profile"을 추가한다.
  • SecurityConfig 설정이 잘 되어있는지 테스트 코드로 검증

    • 스프링 시큐리티 설정을 불러와야하니 @SpringBootTest를 사용하는 테스트 클래스 ProfileControllerTest를 하나 더 추가
  • ProfileControllerTest.java

     

     

real1, real2 profile 생성

  • 현재 EC2환경에서 실행되는 profile은 real 뿐이다

  • Travis CI 배포 자동화를 위한 profile 2개를 추가한다.

  • application-real1.properties

     

     

  • application-real2.properties

     

     

  • server.port가 다른 것을 확인한다.

엔진엑스 설정 수정

  • 배포 때마다 엔진엑스의 프록시 설정(스프링 부트로 요청을 흘려보내는)이 순식간에 교체된다

  • 프록시 설정이 교체될 수 있도록 설정을 추가한다.

  • /etc/nginx/conf.d/ (엔진엑스 설정이 모여있는 곳)에 service-url.inc 파일을 생성한다.

      sudo vim /etc/nginx/conf.d/service-url.inc
    
      --
    
      set $service_url http://127.0.0.1:8080;
    
      --해당파일을 엔진엑스가 사용할 수 있게 설정한다
    
      sudo vim /etc/nginx/nginx.conf
    
      location/ 부분인 이곳을 아래와 같이 변경한다.
    
                      proxy_pass http://localhost:8080         
                      proxy_set_header X-Real-IP $remote_addr;
                      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; #2
                      proxy_set_header Host $http_host;
    
      include /etc/nginx/conf.d/service-url.inc; #include한다
    
              location / {
                      proxy_pass $service_url;   #localhost:8080에서 변경한다.
                      proxy_set_header X-Real-IP $remote_addr;
                      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                      proxy_set_header Host $http_host;
              }
      저장하고 종료한뒤 재시작한다.
      sudo service nginx restart

배포 스크립트들 작성

  • step2와 중복되지 않기 위해 EC2에 step3 디렉토리를 생성

      mkdir ~/app/step && mkdir ~/app/step3/zip
  • 무중단 배포는 앞으로 step3사용 그래서 appspec.yml역시 step3로 배포되도록 수정

     

     

    • Jar파일이 복사된 이후부터 차례적으로 앞선 스크립트들이 실행된다.

무중단 배포 진행할 스크립트 5개

  • scripts 디렉토리에 추가한다.
  1. profile.sh

    • 뒤에 4개 스크립트 파일에서 공용으로 사용할 'profile'과 포트를 체크하는 로직

       

       

    1. RESPONSE_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost/profile)
      • 현재 엔진엑스가 바라보고 있는 스프링부트가 정상적으로 수행 중인지 확인한다.
      • 응답값을 HttpSession로 받는다
      • 정상이면 200, 오류가 발생한다면 400~503사이로 발생하니 400이상은 모두 예외로 보고 real2를 현재 profile로 사용한다.
    2. IDLE_PROFILE
      • 엔진엑스와 연결되지 않은 profile이다
      • 스프링부트 프로젝트를 이 profile로 연결하기 위해 반환한다.
    3. echo "${IDLE_PROFILE}"
      • bash라는 스크립트는 값을 반환하는 기능이 없다.
      • 그래서 제일 마지막 줄에 echo로 결과를 출력 후 , 클라이언트에서 그 값을 잡아서 IDLE_PROFILE=$(find_idle_profile)로 사용한다.
      • 중간에 echo를 사용해서는 안된다.
  2. stop.sh

    • 기존 엔진엑스에 연결되어 있진 않은 실행중이던 스프링부트 종료

       

       

    1. ABSDIR=$(dirname $ABSPATH)
      • 현재 stop.sh가 속해있는 경로를 찾는다
      • 하단의 코드와 같이 profile.sh의 경로를 찾기위해 사용된다
    2. source ${ABSDIR}/profile.sh
      • 자바로 보면 일종의 import구문이다
      • 해당 코드로 인해 stop.sh에서도 profile.sh의 여러 function을 사용할 수 있게 된다.
  3. start.sh

    • 배포할 신규 버전 스프링부트 프로젝트를 stop.sh로 종료한 포트로 'profile'로 실행

       

       

    1. 기본적인 스크립트는 step2의 deploy.sh와 유사하다
    2. 다른 점은 IDLE_PROFILE을 통해 properties 파일을 가져오고(application-$IDLE_PROFILE.properties), active profile을 지정하는 것 (-Dspring.profiles.active=$IDLE_PROFILE)이 다르다
    3. IDLE_PROFILE을 여기서도 사용하니 profile.sh을 가져와야 한다.
  4. health.sh

    • 'start.sh'로 실행시킨 프로젝트가 정상적으로 실행됐는지 체크

       

       

    1. 엔진엑스와 연결되지 않은 포트로 스프링부트가 잘 수행되었는지를 확인한다.
    2. 잘 떴는지 확인되어야 엔진엑스 프록시 설정을 변경(switch_proxy)합니다
    3. 엔진엑스 프록시 설정 변경은 switch.sh에서 수행한다.
  5. switch.sh

    • 엔진엑스가 바라보는 스프링 부트를 최신 버전으로 변경

       

       

    1. echo "set $service_url http://127.0.0.1:${IDLE_PORT};" |
      • 하나의 문장을 만들어 파이프라인(|)로 넘겨주기 위해 echo를 사용한다.
      • 엔진엑스가 변경할 프록시 주소를 생성한다.
      • 쌍따옴표 (")를 사용하여야 한다
      • 사용하지 않으면 $service_url을 그대로 인식하지 못하고 변수를 찾게 된다
    2. | sudo tee /etc/nginx/conf.d/service-url.inc
      • 앞에서 넘겨준 문장을 service-url.inc에 덮어쓴다
    3. sudo service nginx reload
      • 엔진엑스 설정을 다시 불러온다
      • restart와는 다르다
      • restart는 끊김이 잠시 있지만 reload는 끊김 없이 다시 불러온다
      • 다만 중요한 설정들은 반영되지 않으므로 restart를 사용해야 한다.
      • 여기선 외부의 설정 파일인 service-url을 다시 불러오는 거라 reload드로 가능하다

무중단 배포 테스트

  • 잦은 배포로 Jar 파일명이 겹칠 수 있고 매번 버전을 올리는 것이 귀찮으므로 자동으로 버전값이 변경될 수 있도록 한다.

  • build.grdle → version '1.0.1-SNAPSHOT'+new Date().format("yyyyMMddHHmmss")

    1. build.gradle은 Groovy 기반의 빌드툴

    2. Groovy 문법을 사용할 수 있으며 new Date()로 빌드 할때마다 그 시간이 버전에 추가되도록 구성하였다

       

       

  • 깃 허브 푸시 CodeDeploy 로그 진행 확인

      tail -f /opt/codedeploy-agent/deployment-root/deployment-
      logs/codedeploy-agent-deployments.log
  • 정상 작동

      [ec2-user@seongbinko-webservice ~]$ tail -f /opt/codedeploy-agent/deployment-root/deployment-logs/codedeploy-agent-deployments.log
      [2020-03-29 13:18:46.709] [d-6Y3LZ20I2][stdout]> curl -s http://localhost:8082/profile
      [2020-03-29 13:18:56.755] [d-6Y3LZ20I2][stdout]> Health check의 응답을 알 수 없거나 혹은 실행 상태가 아닙니다.
      [2020-03-29 13:18:56.755] [d-6Y3LZ20I2][stdout]> Health check:
      [2020-03-29 13:18:56.755] [d-6Y3LZ20I2][stdout]> Health check 연결 실패. 재시도...
      [2020-03-29 13:19:07.025] [d-6Y3LZ20I2][stdout]> Health check 성공
      [2020-03-29 13:19:07.050] [d-6Y3LZ20I2][stdout]> 전환할 Port: 8082
      [2020-03-29 13:19:07.050] [d-6Y3LZ20I2][stdout]> Port 전환
      [2020-03-29 13:19:07.062] [d-6Y3LZ20I2][stdout]set $service_url http://127.0.0.1:8082;
      [2020-03-29 13:19:07.063] [d-6Y3LZ20I2][stdout]> 엔진엑스 Reload
      [2020-03-29 13:19:07.115] [d-6Y3LZ20I2][stdout]Reloading nginx: [  OK  ]
  • 스프링 부트 로그 확인

      vim ~/app/step3/nohup.out
    
  • 2번 배포 진행한 뒤에 자바 어플리케이션 실행 여부 확인

      [ec2-user@seongbinko-webservice ~]$ ps -ef | grep java
      ec2-user  3445     1  0 Mar15 ?        00:12:36 java -jar -Dspring.config.location=classpath:/application.properties,classpath:/application-**real**.properties,/home/ec2-user/app/application-oauth.properties,/home/ec2-user/app/application-real-db.properties -Dspring.profiles.active=real /home/ec2-user/app/step2/seongbinko-springboot-webservice-1.0.1-SNAPSHOT.jar
      ec2-user 16358     1  0 13:18 ?        00:00:33 java -jar -Dspring.config.location=classpath:/application.properties,classpath:/application-**real2**.properties,/home/ec2-user/app/application-oauth.properties,/home/ec2-user/app/application-real-db.properties -Dspring.profiles.active=real2 /home/ec2-user/app/step3/seongbinko-springboot-webservice-1.0.1-SNAPSHOT20200329041737.jar
      ec2-user 18498 18391  0 19:29 pts/0    00:00:00 grep --color=auto java
      ec2-user 25705     1  0 Mar18 ?        00:10:13 java -jar -Dspring.config.location=classpath:/application.properties,classpath:/application-**real1**.properties,/home/ec2-user/app/application-oauth.properties,/home/ec2-user/app/application-real-db.properties -Dspring.profiles.active=real1 /home/ec2-user/app/step3/seongbinko-springboot-webservice-1.0.1-SNAPSHOT20200318055335.jar

1인 개발 시 도움이 될 도구와 조언들

댓글

  1. Disqus (외국)
  2. LiveRe (국내)
  3. Utterances(github 댓글)

외부 서비스 연동 (ex sns연동)

1. Zapier

2. IFTTT

방문자 분석

 1. 구글 애널리틱스

CDM(Content Delivery Network)

  • 정적 컨텐츠인 JS,CSS,이미지등을 사용자가 서비스에 접속할 때 가장 가까운 서버에서 가져가도록 지원하는 서비스다
  • 클라우드플레어 (ex)

이메일 마케팅(Mailchimp)

1인 개발팁

  • 욕심부리지 말기 핵심기능이 중점
  • 혼자서 개발하기
  • 가장 자신있는 개발 환경을 사용할 것