본문 바로가기
우아한테크코스/Level3

Level3 학습 로그 정리

by 조엘 2021. 8. 26.

안녕하세요! 조엘입니다! 🤞🤞

 

우아한테크코스의 레벨3는 팀 프로젝트입니다! 

저는 놀토라는 멋진 팀에서 "부담없이 자랑하는 작고 소중한 내 토이프로젝트"라는 목표를 가지고 개발하고 있는데요!

 

프로젝트 소개는 더 완성된 이후에 가져오도록 할게요 :)

먼저 궁금하신 분은 놀토 리포로 와주세용 🎺🎺 

https://github.com/woowacourse-teams/2021-nolto

 

이번 레벨 역시 학습로그를 통해 배운 것들을 정리하였는데요. 

저는 이번 레벨에 다음과 같은 주제를 공부했습니다! 

 

JPA 기본

  • 왜 필요한가?
    • 객체를 관계형 DB에 저장하면서 발생하는 문제들
      • SQL 중심의 개발로 변질
      • 패러다임 불일치
        • 상속 유무
        • 연관 관계의 방향성(객체는 자유롭게 객체 그래프를 탐색할 수 있어야 함)
        • 데이터 타입 불일치
    • SQL 쿼리문으로 DB를 조회해 오는 것의 단점
      • 수정의 포인트들이 너무 많아짐
      • 신뢰성이 떨어지는 Dao
      • 반복적인 비슷한 쿼리문
  • 장점
    • 신뢰할 수 있는 Entity
    • Collection을 사용하듯 DB에 저장 (객체지향적)
    • JPA 성능 최적화 가능
      • 1차 캐시, 쓰기 지연, 지연 로딩 등
  • 영속성 컨텍스트
    • 엔티티를 영구 저장하는 환경
    • 하나의 트랜잭션 당 하나의 엔티티 매니저
    • 엔티티 매니저로 엔티티 저장/조회 시 영속성 컨텍스트에 엔티티 보관/관리 (이를 통해 JPA의 기능 사용가능)
      • 1차 캐시
      • 동일성 보장
      • 쓰기 지연
      • 변경 감지
      • 지연 로딩
  • 일대다 연관 관계
    • 일대다 관계에서 "다" 쪽이 FK를 가짐
    • "다" 쪽이 연관 관계의 주인(외래키 관리자)으로써, 외래키를 등록/수정/삭제 가능
      • 외래키를 조작할 때에는 양쪽의 관계를 신경쓰면서 코드를 작성할 것!
    • 일대다 + 양방향 매핑 예시
    • @Entity 
      public class Station { 
          @Id @GeneratedValue 
          private Long id; 
          
          @Column(nullable=false) 
          private String name; 
          
          @ManyToOne @JoinColumn(name = "line_id") //FK로 사용할 칼럼의 이름 
          private Line line;   
       } 
       
       @Entity 
       public class Line { 
          @Id @GeneratedValue 
          private Long id; 
          
          @Column(nullable=false) 
          private String name; 
          
          @OneToMany(mappedBy = "line") 
          private List<Station> stations = new ArrayList<>(); 
      }
  • 링크

 

깃 브랜치 전략

  • 왜 필요한가?
    • 하나의 브랜치로 관리하면 어디에서 에러가 터졌는지 추적이 힘듦
    • 에러를 찾아도 그 이후 개발한 신규 기능 배포가 어려움
  • 놀토의 깃 브랜치 전략
    • main: 실제 제품 배포 브랜치
      • release에서 넘어온 커밋에 태그를 붙여 정식 출시
    • release: 이번 출시 버전 준비를 위한 브랜치
      • 출시하기로한 기능까지 개발이 완료되면, develop에서 파생
      • 실제 배포 환경과 유사한 환경에서 QA 진행
      • 버그 발견 시 hotfix를 활용하여 수정
      • 태그 붙여 main에 머지
    • develop: 다음 출시 버전 개발을 위한 브랜치
      • feature 브랜치를 머지
    • feature: 신규 기능 개발
      • feature/backend/, feature/frontend/ 등으로 기능 개발
    • hotfix: 에러 수정 브랜치
      • release 브랜치의 QA 도중 에러 발생시 해당 브랜치에서 해결
  • 프로젝트를 진행하며 변경된 전략
    • develop에도 실제 배포환경과 유사하게 인프라 구축을 했었음
      • OAuth 및 최신 기능을 프론트단에서 사용하기 위함
    • develop에서 본의아니게 QA가 진행됨
      • 여기서 발생한 버그를 hotfix로 파서 머지시키는 일이 계속 발생함
    • release의 의미가 조금 퇴색되었음
      • 그냥 tag를 붙이는 용도 정도?
  • 머지의 종류
    • Merge
      • 머지 과정에서 개발 시 남긴 커밋을 참조하는 하나의 커밋 생성하고 머지
    • Squash and Merge
      • 머지 과정에서 개발할 시 남긴 커밋을 합쳐 새로운 하나의 커밋으로 만듦
      • "feature -> develop" 개발에서 해당 머지를 사용
      • Feature의 경우 기능개발이 끝나면 삭제하니, 커밋을 굳이 다 남길 필요가 없음
    • Rebase and Merge
      • 그 전까지 했던 작업 이후로 신규 커밋들을 붙임
      • "develop -> release -> master" 개발에서 해당 머지를 사용
      • 머지 과정에서 별도의 새로운 커밋을 생성하지 않아 좋음
  • 링크

 

웹훅

  • 왜 필요한가?
    • 웹훅은 웹앱에서 발생한 이벤트 정보를 "실시간"으로 제공하기 위해서 필요함
    • 클라이언트 측에서 필요한 정보를 서버 측에 계속 물어보는 방식(Polling)이 아닌,
      서버 측에서 클라이언트에게 필요한 정보가 도착하면 알려주는 방식(Webhooks)
  • 사용처
    • 깃헙에서 PR이 머지되면 젠킨스가 해당 리포를 새롭게 빌드하여 배포하도록 설정
      • 젠킨스(클라이언트) <- 깃헙(서버)
    • Error 레벨의 로그가 발생하면 슬랙에 알림 전송
      • 슬랙(클라이언트) <- 놀토 EC2(서버)
  • 링크

 

CI/CD

  • 왜 필요한가?
    • 서버에서 돌릴 수 있는 jar 파일로 만들어 배포를 해야함
      • 하지만 해당 작업은 단순 반복이 많음
      • 반복되는 과정의 자동화 필요 + 휴먼 에러를 없애야 함
    • CI가 왜 필요한가?
      • 팀원들이 나누어 개발하던 것을 하나로 합치는 과정에서 문제 발생할 수 있음
      • 코드 작성이 완료되면 바로 합쳐 오류에 대한 피드백 받을 수 있음
      • 애자일하게 기능 개발을 완료하는 개발 문화와 잘 어울림
    • CD가 왜 필요한가?
      • 실제로 배포하는 과정에서 휴먼 에러가 발생 가능
      • 이를 잡아줌
  • 젠킨스
    • 자바 기반의 자동화 CI/CD 툴
    • 다양한 플러그인을 활용해 효과적인 자동화 가능
  • 놀토 젠킨스
    • 도커에 젠킨스 설치하여 사용
    • Freestyle Project로 구성
    • 플러그인 설치
      • git pull request builder (github 리포에서 코드 pull 받아 올 수 있도록 설정)
      • publish over ssh (jar 파일을 WAS EC2에 전송)
    • 깃헙에서 젠킨스 관련 필요 설정 해주기
    • 젠킨스 workspace/repository/ <- 해당 디렉토리에서 빌드 작업 진행
    • 이후 jar 파일을 WAS EC2에 전송
    • WAS EC2에서 실행시킬 쉘 스크립트를 실행
  • 링크

 

S3

  • 왜 필요한가?
    • 놀토 서비스 특성상 많은 사진들을 저장해야 했음
    • 파일 서버의 역할을 하는 S3가 적격
      • 파일 서버 트래픽 증가 시 장비 증설 대행
      • 저장 파일의 갯수를 계속해서 늘릴 수 있음
    • 다른 파일 서버들과 비교해보진 않았지만 다음과 같은 장점이 있다고 함
      • 내구성이 좋음 (클라이언트 파일 유실 X)
      • 저렴한 비용 (사용한 만큼만 지불)
      • 가용성 (파일 서비스 중단 거의 없음)
      • 보안 (SSL을 통한 안전한 운반)
  • 놀토의 S3 사용법
    • S3 버킷 만들기
      • 유일한 이름으로 만들어야 함
      • 퍼블릭으로 공개하지 않음
    • 따라서 Cloudfront CDN을 통해 접근해야함
      • 어떤 S3 버킷에 대한 CDN인지 설정
      • S3 bucket access "Origin Access Identity"를 사용하여 Cloudfront에서는 S3에 접근 가능하게 설정
        • bucket policy도 곁들여서 업데이트 해주자
  • 스프링에서 S3 버킷에 접근하기
    • access-key, secret-key를 발급하여 BasicAWSCredentials을 활용하여 S3에 접근 가능
      • 하지만 이는 발급 금지
    • WAS EC2에 IAM 권한을 부여하여 S3에 접근 가능하였음
      • ec2-s3-deploy
  • 링크

 

OAuth

 

도커

 

DB Migration

 

로깅

  • 왜 필요하지?
    • 디버깅을 하지 못하는 상황에서 로그를 남김
    • 웹 기반은 사실 로깅에 의존해야하는 경우 다수
      • 서버에 내 프로그램 올려서 디버깅을 할 수 있는 툴이 별로 없어
      • 실서버는 로깅 보면서 해결해야 하는 경우가 다수
  • 놀토의 로깅전략
    • 로깅 레벨
      • FATAL: 매우 심각한 에러, 프로그램 종료되는 경우
      • ERROR: 에러가 발생하지만, 프로그램 종료되지 않는 경우
      • ----------------------의도하지 않은 Exception-----------------------
      • WARN: 에러가 될 수 있는 잠재적 가능성 있는 경우 (ex. 인메모리 캐시 용량 꽉차감, DB 커넥션 예상보다 오래걸림)
      • INFO: 애플리케이션의 상태를 간결하게 보여주는 경우
      • DEBUG: INFO 보다 더 자세한 정보가 필요한 경우
      • TRACE: DEBUG 보다 더 자세한 정보가 필요한 경우, 최종 프로덕션이나 커밋에서 포함하지 말 것
    • XML으로 로깅 설정 작성 후 yml에서 spring profile로 추가하여 사용
      • profile별 어플리케이션 런타임 로깅 전략
        • local: console에 INFO 이상의 로그
        • dev: ERROR 레벨 이상의 로그, INFO 레벨 이상의 로그 각각 nolto-error.log, nolto-info.log에 저장
        • prod: ERROR 레벨 이상의 로그, INFO 레벨 이상의 로그 각각 nolto-error.log, nolto-info.log에 저장
        • dev/prod 환경에서 ERROR 레벨의 로그 찍히면 슬랙에 메시지 전송
      • sql 쿼리 로그 찍기
        • DB에 보낸 쿼리문을 nolto-sql.log에 저장
        • dev 환경에서만 해당 파일 저장
  • 링크

 

트랜잭션

  • 트랜잭션이란?
    • 데이터베이스 트랜잭션은 일관성 있고 신뢰할 수 있는 방식으로 독립적이게 처리되는 작업 단위를 뜻한다.
    • 데이터베이스 트랜잭션은 데이터베이스의 변화를 나타낸다. 데이터베이스 트랜잭션은 크게 두 가지의 목적을 가진다.
      1. 예상치 못한 에러가 발생해도 데이터베이스를 신뢰성 있는 상태로 만들 수 있도록 신뢰할 수 있는 작업 단위를 제공한다.
      2. 데이터베이스에 동시에 접근하는 경우 프로그램 간에 격리를 제공하여 에러를 방지한다.
  • 트랜잭션의 성질
    • 트랜잭션의 성질은 ACID로 대표됨
      • [ Atomicity - 원자성 ]
      • [ Consistency - 일관성 ]
      • [ Isolation - 독립성 ]
      • [ Durability - 지속성 ]
  • 스프링에서의 트랜잭션
    • AOP를 활용한 선언적 트랜잭션 @Transactional
    • 여러가지 옵션을 어노테이션의 속성 값으로 명시함으로써 간편히 사용가능
    • 트랜잭션 전파 설정
      • REQUIRED, REQUIRES_NEW, MANDATORY, NESTED, NEVER, SUPPORTS, NOT_SUPPORTED
    • 트랜잭션 고립 수준
      • DEFAULT, READ_UNCOMMITED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE
  • 링크

 

빈 생명주기

  • 빈 생명주기
    • 스프링의 컨테이너는 빈 객체의 생성/소멸/주입/제공 등을 담당
    • 생명주기는 다음과 같음
      1. 빈 인스턴스화 및 DI
        • XML/자바 설정 클래스/어노테이션으로 빈 정의 스캔
        • 빈 인스턴스 생성
        • 빈 프로퍼티에 의존성 주입
      2. 빈이 인터페이스 구현시 호출
        • 빈이 BeanNameAwate 인터페이스 구현 시 setBeanName() 호출
        • 빈이 BeanClassLoaderAware 인터페이스 구현 시 setBeanClassLoader() 호출
        • 빈이 ApplicationContextAware 인터페이스 구현시 setApplicationContext() 호출
      3. 빈 생성 생명주기 콜백
        • @PostConstruct 어노테이션 적용 메서드 호출
        • 빈이 InitializingBean 인터페이스 구현 시 afterPropertiesSet() 호출
        • 빈이 init-method 정의하면 지정한 메서드 호출
      4. 빈 소멸 생성주기 콜백
        • @PreDestory 어노테이션 적용 메서드 호출
        • 빈이 DisposableBean 인터페이스 구현시 destory() 호출
        • 빈이 destory-method 정의하면 지정한 메서드 호출
  • 링크

 

JPQL

  • 왜 필요하지?
    • 필요한 정보를 가져오기 위해 .get().get() 을 통해 가져오는 것에 대한 비효율성
    • 필요한 데이터를 DB에서 가져오려면 검색 조건을 명시한 SQL 쿼리의 필요성 대두
  • JPQL?
    • JPA는 SQL을 추상화한 JPQL이란 객체 지향 쿼리 제공
      • SQL과 문법이 유사
      • JPQL은 엔티티 객체를 대상으로 쿼리를 질의하고, SQL은 데이터베이스 테이블 대상으로 쿼리를 질의
    • 테이블이 아닌 객체를 대상으로 검색하는 객체지향 쿼리!
      • SQL을 추상화했기에 특정 DB 벤더에 의존 X
  • 링크

 

자기참조 테이블

  • 자기 참조 관계
    • 하나의 엔티티가 다른 엔티티가 아니니 자기 자신과 관계를 맺는 타입
      • 동일 엔티티 타입 내에서 자기가 자기의 인스턴스를 참조하는 구조
    • 계층 구조를 나타내기에 유용
      • 놀토에서는 댓글-대댓글에 사용
      • FK로 연결하지 않고 상위 계층의 id는 자기 참조를 통해 나타냄
  • 링크

 

JPA N+1 문제

 

Spring EventListener & TransactionalEventListener

반응형

댓글2