분류 전체보기 (103)

 

 

git 브랜치 관리법이 복잡하여 따로 정리해둡니다. 😄

 

로컬에 git 관리하에 없는 디렉토리에 특정 브랜치만 clone 하기

git clone -b {branch 이름} {remote 주소}

 

 

새로운 브랜치를 만들고 현재 작업중인 브랜치를 전환하기

git checkout -b {branch 이름}
git switch -c {branch 이름}

 

 

브랜치 전환하기

git checkout {branch 이름}
git switch {branch 이름}

 

 

commit 자세히 남기기

git commit

 

 

스테이지에 올라간 파일 내리기(언스테이징)

git reset {file 이름}

 

 

stash에 현재 변경 사항, 스테이징된 파일 임시 저장하기

git stash

 

 

stash에 있는 변경 사항 적용하기

git stash apply

 

 

stash에 있는 변경 사항 적용하고 삭제하기

git stash pop

 

 

stash에 임시 저장된 변경 사항 확인하기

git stash list

 

 

트랙에 있는 언스테이징된 파일 캐시 지우기 (트랙 초기화)

git rm -r --cached .

 

 

현재 브랜치 원격 저장소에 push 하기

git push {원격 저장소 이름} {branch 이름}

 

 

현재 작업중인 브랜치가 아닌 다른 원격 저장소 브랜치에 push 하기

git push {원격 저장소 이름} {로컬 브랜치 이름}:{원격 브랜치 이름}

 

 

git push할 때 기본값 지정하기

git push -u {원격 저장소 이름} {branch 이름}

➡️ 이후엔 같은 저장소 같은 브랜치로 push 할 때는 'git push' 명령어로 가능

 

 

강제로 pull 하기

git pull -f

 

 

 

 

'GIT' 카테고리의 다른 글

[GIT] 브랜치 Merge(병합) 전략  (0) 2022.09.23

 

 

 

 

EC2 인스턴스를 종료하면 인스턴스가 삭제된다❗️❗️❗️❗️

 

 

AWS를 팀원과 설정하고 생성한 이후에 팀원에게서 "ec2 종료 누르면 삭제되니까 끄실 때는 중지하셔야 될거에요" 라는 말을 들었다. 😱

Pre Project 진행하면서 free-tier 사용 중이라 서버 테스트 제대로 하기 전까진 꺼두려고 했다...

그런데 설정 다 마치고 서버 올렸는데 여기서 "인스턴스 종료" 를 눌렀으면... 다시 설정해야하는 귀찮음을 겪었을 것이다.

웃기게도 aws의 '인스턴스 종료'는 우리가 아는 컴퓨터 종료가 아니라 삭제의 개념이다!

인스턴스를 잠시 끄는 것은 '인스턴스 중지' 이다.

왜 헷갈리게 삭제를 종료라고 사용하는지는 모르겠다...

 

AWS 공식 문서에서도 인스턴스를 종료한 이후에는 해당 인스턴스에 연결하거나 재시작할 수 없다고 써져있다.

AWS는 왜이렇게 복잡하게 해놨는지 모르겠다.

자사 서비스를 쓰려면 공부를 하고 쓰라는건가..?

AWS 자격증을 따야지만 쓸 수 있는 겁니까????!!!!!

 

유명한 aws 밈

 

 

심지어 인스턴스 생성할 때 과금안하게 설정하려면 많은 조사가 필요했다. 그래서 전혀 과금이 안되게 만들었다. (대신에 퍼포먼스가 너무 심각하다... 이건 따로 다시 포스팅을 할 예정이다.)

 

 

 

결론

 

혹시라도 이 글을 보시는 분들은 실수라도 "인스턴스 종료"를 눌러서 인스턴스를 날리지 않도록 조심하세요.

EC2 서버를 잠시 끄려면 "인스턴스 중지" 상태로 놓고 다시 "시작"을 하세요...!!

 

 

 

'AWS' 카테고리의 다른 글

AWS VPC? Subnet?  (0) 2022.09.27

 

 

 

▶️ 테이블 DROP

 

테이블 삭제 시 외래키 제약 조건이 걸려 있다면 그대로 삭제하려하면 외래키로 등록되어 있는 테이블 먼저 삭제하라는 에러가 발생한다.

그래서 테이블 삭제 시 먼저 외래키 제약 조건을 확인할 필요가 없도록 설정을 바꿔줘야 한다.

SET FOREIGN_KEY_CHECKS = 0;

이제 테이블을 삭제해준다.

DROP TABLE [talbe명];

삭제할 테이블을 모두 삭제했다면 다시 외래키 제약 조건 확인을 기본값으로 돌려놓는다.

SET FOREIGN_KEY_CHECKS = 1;

 

 

 

▶️ Query 결과 예쁘게 출력하기

 

터미널같은 CLI 환경에서 쿼리문으로 데이터를 조회하면 칼럼이 여러개인 경우나 데이터가 길게 나오면 보기가 불편하다.

이럴때는 쿼리문 마지막에 세미콜론(;) 대신 \G 를 입력하면 쿼리 결과가 예쁘게 출력되서 보기 편하다!

SELECT * FROM [table명]\G

실행 결과

 

 

 

 

 

 

 

오개념, 오타에 대한 지적은 언제나 환영입니다~ 😄

 

 

프로젝트를 진행하면서 처음보는 오류를 발견하였다. 🙄

LazyInitializationException

같은 백엔드 팀원분과 서칭해서 반나절만에 해결할 수 있었다. 

 

환경 : AWS ec2 인스턴스에서 서버 배포 + Spring Data JPA(hibernate) + MySQL

 

어떤 오류?

2022-09-02 06:59:34.637 ERROR 35806 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.lucky7.stackoverflow.question.entity.Question.comment, could not initialize proxy - no Session] with root cause

org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.lucky7.stackoverflow.question.entity.Question.comment, could not initialize proxy - no Session
        at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:614) ~[hibernate-core-5.6.10.Final.jar!/:5.6.10.Final]

요약하자면 Lazy(지연) Initialization(초기화) 오류이다.

프록시를 초기화할 수 없다고 나오며 no Session이라고 세션이 없다는 로그가 보인다.

 

 

오류가 발생하는 이유?

 

일단 Spring Data JPA의 Lazy 전략에 대한 이해가 부족하였고 영속성 컨텍스트의 생명주기에 대해 알지 못하여서 발생하는 문제였다.

간단하게 정리해보자면 이렇다.

  • 서비스 단에 @Transactional을 붙여놓은 상태여서 메서드가 종료되면 Hibernate의 Session도 함께 종료되어 영속성 컨텍스트에서 사라진다.
  • 영속성 컨텍스트는 보통 트랜잭션과 생명주기를 같이한다. → service 호출 끝나고 controller로 돌아가면 영속성 상태가 끝난다.
  • jpa의 효율 문제 때문에 엔티티가 List나 객체로 참조하고있는 부분을 전부 쿼리문을 날려서 채워놓진 않고 일단 프록시 객체로 채워놓은 이후 나중에 getter를 사용하면 쿼리를 보내서 실제 데이터로 채운다.
  • Question에 조회하는 요청 시 owner나 comment, answer 에 실제 데이터를 쿼리를 날려서 저장하는 것이 아닌 프록시 객체로 채워진다.(stub 데이터라고 보면 됨) -> 프록시 객체로 채워진 상태에서 Service 메서드 호출이 끝나면 영속성 컨텍스트의 관리대상에서 제외되기 때문에 이후엔 gatter를 사용해도 쿼리문을 날려서 해당 객체를 실제 데이터로 채우지 않는다.
  • QuestionService에 @Transactional 애너테이션 붙어있음 → controller에서 service의 메서드 호출한 순간부터 트랜젝션 생성 → 서비스 메서드 끝나면서 트랜젝션 종료됨 → mapper에서 question객체를 dto 객체로 변환시켜주면서 owner, comment, answer 객체를 불러오는 쿼리 보냄 → 영속성 컨텍스트에 존재하지 않아서 프록시 객체를 실제 객체로 채우지 못함 → LazyInitializationException !

 

 

참고 자료 : https://cantcoding.tistory.com/78

 

 

 

해결 방법

 

mapper에서 entity 객체에 gatter 메서드를 호출하는 부분까지 트랜잭션 안으로 포함시켜주면 해결할 수 있다.

-> controller 에 @Transactional 애너테이션 붙여서 mapper 사용하는 부분까지 전부 하나의 트랜잭션으로 포함시켜 주었다.

이렇게 하면 http 요청이 와서 컨트롤러에 메서드가 호출하는 시점부터 트랜잭션이 생성되고 서비스에서 저장이나 삭제를 처리하고 다시 컨트롤러로 돌아와서 매퍼를 호출할 때까지도 트랜잭션이 유지되기 때문에 영속성 컨텍스트에서 관리하는 상태가 된다.

그리고 컨트롤러에서 응답을 보내게 되면(return) 메서드 호출이 끝나므로 트랜잭션이 종료된다.

 

 

아직은 이해가 깊지 않아 이게 가장 좋은 방법인지는 모르겠다.

하지만 해결법이 간단하고 성능상의 문제 없이 해결할 수 있었고 Spring Data JPA라는 녀석에 대해서 한걸음 더 이해할 수 있었다.

 

 

 

 

👉🏻 기초 문서작업 (뼈대) 완성

 

프로젝트가 다가올 수록 문서작업에 대한 고민이 많았다.

생각나는 거라고는 데이터베이스 diagram(ERD)과 API 문서 정도였다.

다행히 코드스테이츠에서 프로젝트 팀이 결성되고 바로 문서에 대한 가이드를 제공해 주었다.

어떤 문서를 작성하는 것이 좋고 어떤 방식으로 하면 좋은지 프론트, 백엔드 모두 이해할 수 있도록 안내를 받았다.

프론트엔드 개발자가 작성해야하는 문서에 대해서는 몰랐는데 화면정의서라는 것을 처음으로 알게 되었다.

백엔드에서 작성해야할 문서는 API 명세서데이터베이스 명세서이다.

어제 작성을 마친 사용자 요구사항 정의서를 토대로 API 명세서를 먼저 작성하였다.

처음엔 API 명세서는 프론트 엔드 팀원분들과 같이 작성을 하다가 백엔드에서 작성하고 넘기는게 효율적일 것 같다는 의견이 나와서 백엔드 팀원분과 같이 작성했다.

API 명세서를 반드시 Spring Rest Docs를 사용할 필요는 없고 데이터베이스 명세서도 반드시 ERD로 만들 필요는 없다고 해서 문서화작업을 최대한 빠르게 마치고 코딩으로 넘어가기 위해서 간단하게 필수기능만 작성하기로 했다. 

데이터베이스 명세서는 API 명세서의 데이터를 토대로 작성하는 거라서 다른 팀원분이 ERDcloud 라는 쉽고 직관적인 사이트를 알려주셨다. 내가 알던 dbdiagram.io는 테이블과 칼럼, 속성 등을 sql문으로 작성해야 했는데...ERDcloud는 너무 편했다. 엔티티를 그리는 것도 편하고 작업을 개인이 아닌 팀으로 설정하면 팀원을 추가해서 같이 작업할 수 있다! 데이터베이스 명세서는 백엔드 다른 팀원분이 맡아서 해주신다하셔서 너무 감사했다. (안그래도 할일이 밀린데다 오랜 회의로 피로가 많이 누적되 있었다.) 그리고 몇 시간전에 완성된 것을 확인했다. 😄 

프론트엔드 팀원분들도 화면정의서를 필수기능 위주로 간단하게 작성해주셨다. 사실 굉장히 빠르게 마치셔서 놀랐다. 우리 팀의 프론트 분들의 작업속도가 빠른 편인 것으로 보인다. ㅎ 

다행히 문서 작업이 예상보다 굉장히 빠르게 끝났다!!!

우리의 목표는 이번주 내로 끝내는 거였는데 시작한지 몇일 안된 오늘 (수요일)에 끝났다..!ㅠㅠ 팀원분들 만세ㅠㅠ

 

 

👉🏻 5명으로 시작해서 몇일만에 4명으로 줄었다..ㅠ

 

저번주 금요일에 프로젝트 팀이 정해지고 이번주부터 제대로 프로젝트에 돌입했는데 팀원 한분이 자리를 비우고 회의에도 참석하지 않았는데 하차하신다는 연락을 받았다.ㅠㅠ 사정이 있어서 그런거지만 아쉬운 건 어쩔 수가 없었다. 다른 팀에선 1명이 하차해서 공중분해 되서 다 다른 팀으로 배정되었다는 얘기를 들었는데... 우리는 다행히 한명이 하차해도 백엔드 2명, 프론트엔드 2명으로 다른 팀과 인원차이가 안나기 때문에 그대로 진행할 수 있었다.

 

 

 

 

 

 

🍉 GitHub 레포지토리에 꼭 필요한 파일

 

 

🥝 README.md

포함할 정보들

  • 프로젝트 이름
  • 팀원 소개
  • 핵심 기능 소개

 

 

🥝 .gitignore

git으로 관리하지 않는 파일 모음

개인이 관리해야하는 secret token, 공유할 필요없는 설정 파일 등을 추가

 

 

🥝 LICENSE

라이센스 표기

오픈소스 소프트웨어 개발은 보통 제약이 적은 MIT 라이센스나 Apache License 적용

다양한 라이센스가 있어서 필요한 걸로 적용

 

 

 

🍉 프로젝트 관리에 활용할 수 있는 GitHub 기능들

 

1. Issue

새로운 기능을 제안하거나 버그를 제보하는 등 프로젝트 이슈를 올리는 곳

Pre-Project에서 하나의 칸반 티켓으로 사용하면 된다.

 

 

2. Milestone

이정표 역할

task 카드(Issue)를 그룹화하는데 사용

연결된 태스크 카드가 종료되면 Milestone마다 진행 상황이 업데이트

 

 

3. Pull Request

작업한 내용을 branch에 합칠 수 있는지 확인하는 요청

커밋한 코드를 따로 선택하여 해당 부분에 코멘트(코드 리뷰)를 달 수 있음

머지의 규칙을 정할 수도 있음

 

 

4. Project

업무 관리를 해줄 수 있게 돕는 새로운 기능

칸반 보드를 생성하고 칸반으로 Pre-Project의 업무 흐름을 관리

 

 

칸반 보드란?

업무를 효율적으로 관리하기 위한 시각적 프로젝트 관리의 한 형태

칸반이란? 팀이 수행해야 하는 업무와 각 팀원이 맡을 수 있는 작업량 간에 균형을 맞추는 수단

칸(看, Kan, 신호), 반(板, Ban, 판)

칸반보드에서 업무를 열로 구성된 프로젝트 보드에 표시한다.

각 열은 업무 단계를 나타냄

기본적인 틀 : 할 일, 진행 중, 완료

개별작업은 보드에서 비주얼 카드로 표시

 

출처 : asana

 

 

 

✅ Work In Progress(WIP)로 흐름 관리

 

진행 중인 업무의 개수를 제한하여 과도하게 업무가 쌓이지 않아 원활한 업무 흐름을 만들어 낼 수 있다.

팀원이 100%의 리소스를 사용하는 상태에서 새로운 업무가 쌓이게 되면 업무 과부하로 효율이 안나올 수 있다.

단기간에 많은 아웃풋을 내려고 하면 금방 지쳐서 번아웃이 올 수 있으므로 적당하게 분배하자.

업무 개수를 적절하게 정해서 맥락 전환(context switching) 없이 현재 하고 있는 프로세스에 집중할 수 있게 하여 업무 효율이 증가한다.

 

 

 

✅ 명확한 팀 정책 설정

 

칸반을 잘 활용하기 위해서는 정책을 설정해야 한다.

언제든 팀원이 모여서 다시 조정 가능하다.

 

 

✅ 회의와 리뷰를 통한 업무 개선

 

데일리 칸반 회의

주간 보충 회의

월간 또는 주간으로 KPT 회고를 진행하는 것도 좋음

 

 

 

🍉 GitHub Milestone

 

GitHub 마일스톤 튜토리얼

 

여러가지 이슈들을 그룹화해서 진척도를 한눈에 볼 수 있다.

 

예) Bare Minimum Requirement, Advanced Challenge

 

 

 

🍉 변경사항 PR 하기

 

🥝 Pull Request 시 체크하면 좋은 것들

  • title이 올바른 이슈 타입인가
  • 특정 이슈를 해결할 때, PR의 제목에 issue 넘버가 적혀있는가 (ex. fix #12)
  • 커밋 제목이 가이드라인을 따랐는가
  • 변경사항의 테스트가 추가되었는가
  • 문서들이 추가되거나 업데이트 되었는가
  • breaking change를 소개하지 않거나 breaking change에 대한 설명이 있다. (? 무슨말인지 잘 이해가 안간다)

👆🏻 출처 nhn tuieditor

위와 같은 자료들을 참고해서 Pull Request 에 대한 가이드라인이나 체크리스트를 작성해두는 것이 좋다.

 

 

새로운 branch 를 만들어서(feat: xxx) main  브랜치의 변경없이 새로운 기능을 구현한 후 마치면 main(또는 dev) 브랜치에 합친다.

merge 된 branch는 기본적으로 삭제하지만 나중에 다시 볼 일이 있다면 삭제하지 않아도 된다.

 

 

🥝 브랜치 생성하기 (create 옵션 -c)

 

git switch -c feature/sign-up
git checkout -c feature/sign-up

 

 

🥝 기존 브랜치로 옮기기

 

git switch main
git checkout main

 

 

🥝 main 브랜치에 병합하기

 

git merge feature/sign-up

 

 

🥝 merge된 브랜치 삭제하기(delete 옵션 -d)

 

git branch -d feature/sign-up

➡️ branch가 합쳐지지 않으면 삭제하지 못하도록 설정되어 있음.

merge 하지 않고 삭제하고 싶으면 -D 옵션을 사용하면 됨

 

 

 

🍉 Git 헷갈리는 명령어

 

git reset [file]

-> 추가한 파일을 언스테이징, 변경사항은 유지됨

 

git diff

-> 스테이지되지 않은 변경 사항을 보여줌

 

git diff --staged

-> 스테이지했지만 커밋하지 않은 변경사항을 보여줌

 

git branch

-> 현재 브랜치 목록을 보여줌(*표시가 현재 작업중인 브랜치)

 

git branch [branch-name]

-> 현재 커밋에서 새로운 브랜치를 생성한다.

 

git branch -c [branch-name]
git checkout -b [branch-name]

-> 새로운 브랜치를 생성하고 작업을 해당 브랜치로 전환한다.

 

git merge [branch-name]

-> 현재 브랜치에 특정 브랜치를 병합한다.

 

git log

-> 현재 브랜치의 모든 커밋 히스토리를 보여줌

 

git log B..A

-> 브랜치B에 없는 브랜치A의 모든 커밋 히스토리를 보여줌

 

git log --follow [file]

-> 해당 파일의 변경 사항이 담긴 모든 커밋을 표시(파일 이름 변경도 표시)

 

git diff B...A

-> 브랜치A에는 있지만 브랜치B에 없는 것의 변경내용(diff)을 표시한다.

 

git remote add [alias] [url]

-> url을 통해 특정 원격 저장소를 별칭으로 추가한다.

 

git fetch [alias]

-> 별칭으로 추가한 원격 저장소에 있는 모든 브랜치 및 데이터를 로컬로 가져옴

 

git merge [alias]/[branch]

-> 리모트 브랜치를 현재 작업중인 브랜치와 병합하여 최신상태로 만듬

 

git push [alias] [branch]

-> 로컬 브랜치의 커밋을 리모트 브랜치에 전송함

 

git pull

-> 원격 저장소의 정보를 가져와 자동으로 로컬 브랜치에 병합 (pull = fetch + merge 이라서 pull을 주로 사용)

 

git rebase [branch]

-> 특정 브랜치의 분기 이후 커밋을 현재 작업중인 브랜치에 병합 (merge와 비슷하지만 다르다.)

 

git reset --hard [commitish]

-> 특정 커밋 전으로 돌아가며 스테이지된 변경 사항을 모두 지움

 

git stash

-> 수정하거나 스테이지 된 변경사항을 스택에 임시 저장하고 현재 작업 내역에서 지움

 

git stash list

-> 스택에 임시 저장된 변경사항의 목록을 보여줌

 

git stash apply

-> 스택에 임시 저장된 변경사항을 현재 작업 내역에 적용함

 

git stash pop

-> 스택에 임시 저장된 변경사항을 현재 작업 내역에 적용하고 스택에서 삭제함

 

git stash drop

-> 스택에 임시 저장된 변경사항을 삭제함

 

 

🍉 Git flow

 

유명하고 많이 쓰이는 branch 전략이다.

 

🥝 Coz’s Git flow

-> Git flow를 단순화

 

🥝 Coz’s Git flow 브랜치 전략

 

  • main : 사용자에게 언제든 배포할 수 있는 브랜치
  • dev : 다음 버전 배포를 위한 "개발 중" 브랜치
  • feat/작업이름 : 기능 개발, 리팩토링, 문서 작성등을 위한 브랜치

 

branch에 대한 최소한의 기준을 팀원과 상의할 필요가 있음!

 

어떤 때 dev -> main 으로 merge 할지?

예시) 대표적 기능 완성, 기획한 레이아웃과 전체적 디자인 얼추 완성, 클라이언트-서버-데이터베이스가 공개된 웹에서 정상적으로 통신, 최소한 보안 마련

 

 

⭐️ 실제 프로젝트 때는 브랜치를 로컬에서 합치기 보다 pull request 해서 같이 코드 리뷰를 충분히 하거나 확인을 하고 머지하는 것이 바람직하다. (로컬에서 머지 ❌, 브랜치 PR 요청 ✅)

 

✔️ branch는 기능별로 만들고 기능별로 담당자를 선정한다.

 

 

개발환경 구성(Back-End)

1. 통합 개발 환경 선택 (IDE)

IntelliJ

 

2. JDK 선택

AdoptOpenJDK가 많이 사용됨 (표준은 아님)

버전 11 지정

 

3. Framework 선택

Spring-boot 사용

버전은? 2.7.1 버전으로 지정

 

4. build 방식 선택

Gradle 방식 사용

-> XML 방식으로 작성하는 Maven보다는 Groovy 방식으로 작성하는 Gradle이 작성 및 문제 해결 시 용이

 

5. 형상관리(Software Configuration Management) 방법 결정

버전관리 및 개발 협업을 위한 형상관리

GIthub

 

 

 

 

 

😜 프로젝트를 위한 Github 사용법 학습

 

오늘은 팀 프로젝트에서 GitHub를 어떻게 활용할 수 있는지 배웠고 사용법을 자세히 배웠다.

프로젝트 기간이지만 내일 낮까지는 프로젝트를 위한 학습이 진행된다.

Github를 개인 코드 저장소로만 이용해서 다른 사람과 같이 관리해본 적이 없어서 오늘 내용을 최대한 집중해서 익혔다.

 

 

🔎 칸반 보드?

 

오늘 배운것 중에 가장 생소한 단어였다.

영어 같진 않아서 찾아보니까 일본어이다. (看板, 칸반)

칸반 보드 생긴 걸 보니까 많이 봤던 거다.

할일과 진행 중인 프로세스, 그리고 끝난 프로세스를 한눈에 시각화 하는 가상 보드이다.

칸반 보드를 실무에서는 Jira(지라)같은 툴을 이용한다고 한다.

지금은 Github Project 탭이랑 노션을 잘 활용하면 충분할 것 같다.

 

 

🏃‍♂️ 운동은 꾸준히...

 

지금까지 나름 컨디션 관리를 잘 했다고 생각했다.

지금까지 밤에 적게 자고 학습이 가능했던건 전적으로 운동 덕분인 것 같다.

그런데 일주일 동안 프로젝트 전에 복습하겠다고 운동을 안했더니 몸이 많이 쳐졌다..

운동을 꾸준히 했을 때랑 쉬었을 때의 차이는 낮에 공부할 때 많이 느낀다. 🥱

체력이 떨어지면 오래 앉아서 작업하기가 힘들다.

계속 졸음이 쏟아지고 몸이 여기저기가 뻐근하다. 🫠

그래서 오늘부터 다시 저녁 운동을 시작했다.

이제 프로젝트 동안 최소 일주일에 3회 운동해서 체력을 보존해야 겠다.

 

 

 

🌱 주말동안 깃 프로필을 꾸며보았다.

 

 

저번 주말동안 그동안 미뤄왔던 Git 프로필 ReadMe.md 파일을 커스텀 해보았다.

인터넷에 검색해가면서 대충 했는데 나름 예쁘게 된 것 같다.

오늘 보니까 뱃지도 2개나 더 늘었다. pull shark랑 quickdraw 이다.

둘다 혼자 실습하다보니까 자동으로 생겼다. ㅋㅋㅋ

 

 

 

 

 

 

회고 카테고리를 만들고 처음으로 회고록을 작성한다.

프로젝트 들어가면서 아무래도 있었던 일이나 소프트 스킬 부분을 정리할 필요가 있겠다고 생각했다.

 

 

오늘은 드디어 학습을 마치고 Main-Project 전에 Pre-Project 를 시작했다.

첫날부터 코딩이나 설계같은 걸 하진 않았고 일단 오리엔테이션으로 하루가 지나갔다.

 

이제 팀으로 진행하기 때문에 커뮤니케이션에 대해서 하루종일 설명을 들었는데 오랜만에 쉬는 타임이라 편하고 즐거웠다.ㅋㅋㅋ

 

팀 매칭이 되서 드디어 2달의 대장정을 함께할 팀원이 발표되었다.

우리 팀은 나를 포함해서 총 5명인데 7팀이라서(행운의 7) 시작전부터 뭔가 느낌이 좋았다.

팀 매칭이 되고 팀원들과 첫 회의를 하는 시간을 가졌다.

다들 열정으로 가득하신 것 같아서 다행이었다.

지금은 프로젝트에 대한 걱정보단 기대가 많이 큰것 같다.

앞으로의 여정이 기대된다.

 

 

팀의 팀장을 맡게 되었다. 🥸

 

 

먼저는 팀장의 역할을 정하고 팀장을 정했다.

어쩌다보니 내가 팀장이 되었다.

일단 맡게 되었으니 운명이라 생각하고 최선을 다해서 최고의 결과물을 내야겠다,,,!

우리는 팀장의 역할을 이렇게 정했다.

  • 회의 주제를 미리 선정 및 준비 : 회의 10분 전에 미리 화상채팅방 준비하기
  • 커뮤니케이션 조율 : 의견 충돌 시 중재

 

그리고 앞으로 연락할 커뮤니케이션 툴과 화상회의를 할 툴에 대해서 의논했다.

slack 이라는 디스코드와 비슷한 툴이 있는데 커뮤니케이션 툴은 그걸로 결정했다.

아무도 슬랙을 써본적이 없었지만 현업에선 많이 사용한다고 해서 경험해볼겸 일단 써보기로 했다. (써보고 불편하면 디코로 옮기기로 했다.ㅋㅋㅋ)

그리고 게더타운을 화상회의 툴로 결정했는데 이것도 아무도 사용해본 적이 없었다.

모두들 새로운 기술에 적극적이라 감사했다.

 

 

팀의 룰의 대략적 뼈대를 만들었다.

 

코드스테이츠에서 여러 자료를 제공해주기도 했고 팀원분이 좋은 자료를 준비해주셔서 룰을 만들때 여러 의견들이 나왔다. 일반 규칙과 개발 관련 규칙으로 나눠서 정했다.

일반 규칙은 회의 시간, 회의할 때 순서대로 발언하기, 상시연락 가능 시간(책상앞에 있는 시간)을 정했다.

개발 관련규칙엔 "혼자 알지 않기" 와 "혼자 모르지 않기"를 추가했다. 

지금까지는 혼자 생각하면 됐지만 처음으로 팀으로 움직이는 거라 사소한것 하나까지도 공유해야 하고 오버커뮤니케이션이 필요하다.

 

회의 시간은 오전과 오후로 나눠서 작업 전후로 회의를 가지기로 했다.

오전에는 전날 작업했던 내용의 대략적 정리와 오늘 할 내용을 간단하게 공유하기로 했고

오후엔 그날 한 작업에 대한 리뷰와 정리, 회고를 하기로 했다.

우리는 회고를 KPT(Keep, Problem, Try) 형식으로 진행하기로 결정했다.

 

화상회의가 끝난 후에 감사하게도 적극적으로 의견과 피드백이 오가서 난 팀운이 굉장히 좋다고 생각했다. 사실 개인적으로 코딩 스킬보다 커뮤니케이션 스킬이 좋은 팀원을 만나고 싶었는데 참 다행이고 감사하다.

앞으로의 프로젝트 대장정을 위해 프로젝트 들어가기 전까지 준비를 많이 해야겠다.

 

 

 

 

 

 

Rest Docs 기능 활성을 위해 build.gradle 파일에 코드를 추가하고 build 하니까 자꾸만 찾을 수 없다고 떠서 이번 기회에 build.gradle 에 추가해야 하는 코드를 정리해 놓으려고 한다.

버전 정보는 바뀔 수 있으므로 불러오기에 실패할 경우 버전을 체크하자.

 

plugins {
	id 'org.springframework.boot' version '2.7.2'
	id 'io.spring.dependency-management' version '1.0.12.RELEASE'
	id 'org.asciidoctor.jvm.convert' version '3.3.2'
	id 'java'
}

group = 'com.wisejade'		// initializr에서 프로젝트 생성시 설정한 group이 자동으로 들어감
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

repositories {
	mavenCentral()
}


ext {
	set('snippetsDir', file("build/generated-snippets"))	// 추가해야하는 코드
}

configurations {
	asciidoctorExtensions		// asciidoctorExtensions를 사용하기 위해 추가
}

dependencies {
	testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc'	// mockmvc 사용을 위해 추가
	asciidoctorExtensions 'org.springframework.restdocs:spring-restdocs-asciidoctor'	// asciidoctor
	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'	// Spring data jpa 사용을 위해 추가
	implementation 'org.springframework.boot:spring-boot-starter-validation'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	compileOnly 'org.projectlombok:lombok'
	runtimeOnly 'com.h2database:h2'
	annotationProcessor 'org.projectlombok:lombok'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	implementation 'org.mapstruct:mapstruct:1.5.2.Final'					// mapstruct 사용을 위해 추가
	annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.1.Final'		// mapstruct 사용을 위해 추가
	implementation 'org.springframework.boot:spring-boot-starter-mail'

	implementation 'com.google.code.gson:gson'	// gson은 Json을 String으로 쉽게 바꿔줌
}

tasks.named('test') {	// 추가해야하는 코드
	outputs.dir snippetsDir
	useJUnitPlatform()
}

tasks.named('asciidoctor') {	// 추가해야하는 코드
	configurations "asciidoctorExtensions"
	inputs.dir snippetsDir
	dependsOn test
}

task copyDocument(type: Copy) {	// 생성된 rest docs를 static/docs로 자동으로 copy
	dependsOn asciidoctor
	from file("${asciidoctor.outputDir}")
	into file("src/main/resources/static/docs")
}

build {	// task build 할 때마다 copy
	dependsOn copyDocument
}

bootJar {
	dependsOn copyDocument
	from("${asciidoctor.outputDir}") {
		into 'static/docs'
	}
}

 

 

 

 

'SpringBoot > Spring 기초' 카테고리의 다른 글

[SpringBoot] JPA ddl-auto 옵션 정리  (0) 2022.07.19

 

 

 

 

 

 

🍙 Spring Security의 인증 처리 흐름

 

 

Authentication flow

 

 


 

 

🍙 Authentication(인증)

 

 

🍘 Authentication 인터페이스

 

🍥 Spring Security에서 Authentication은 인터페이스로 존재한다.

🍥 사용자가 인증을 성공적으로 수행하면 사용자의 인증정보를 가지고 있는다.

🍥 SecurityContextHolder는 SecurityContext를 호출하고 SecurityContext는 Authentication 객체를 갖고 있다.

🍥 일반적으로 많이 사용하는 구현체는 UsernamePasswordAuthenticationToken 이다.

➡️ 사용자의 고유 식별자(Username)과 암호(Password)로 간단하게 Authentication 객체를 생성할 수 있다.

 

 

🍘 Spring Security내에서의 Authentication 주요 목적

 

  • 사용자가 인증을 위해 제공한 Credential(자격증명)을 제공하기 위해 AuthenticationManager에 대한 입력값이다.
  • 현재 인증된 사용자를 나타낸다. 현재 Authenticaion은 SecurityContext에서 얻을 수 있다.

 

 

 

🍘 Authentication 에 포함된 정보

 

🍰 principal

  • 사용자를 식별한다.
  • UserDetails 인터페이스 구현체는 사용자의 username과 password로 인증이 이루어진다.
  • 구현체 위치 : org.springframework.security.core.userdetails.User
  • 직접 UserDetails를 상속받아 구현할 수도 있다.

 

🍰 credentials

  • 대부분 password 이다.
  • 대부분의 경우 사용자 안증 후에 유출되지 않도록 삭제된다.

 

🍰 authorities

  • AuthenticationManager에 의해 부여된 인가에 대한 정보이다.
  • 부여된 권한에 대한 정보는 GrantedAuthority로 추상화한다.
  • 일반적으로 사용하는 구현체는 SimpleGrantedAuthority 이다.

 

 

 

🍘 Spring Security 에서 Authentication

 

  • Spring Security 는 종합적인 인증처리를 지원한다.
  • 인증은 특정 리소스에 접근하려고 하는 사용자가 누구인지 확인할 때 사용한다.
  • 보통 사용자가 이름과 비밀번호를 입력하는 것으로 사용자를 인증한다.
  • 한번 인증하면 사용자를 식별하고 권한을 부여할 수 있다. (Authorization, 인가)

 

 

 

🍘 Password Storage

 

Spring Security의 PasswordEncoder 인터페이스는 비밀번호를 안전하게 저장할 수 있도록 단방향 변환을 수행해준다.

  • PasswordEncoder 는 비밀번호를 단반향(비밀번호 -> 암호화)으로 변환, 양방향 변환 목적 x
  • 인증에 사용할 credential 정보를 저장한다.

PasswordEncoder 를 사용해서 저장하는 비밀번호는 인증 시점에 사용자가 입력하는 비밀번호와 비교하는 용도이다.

 

 

 

🍘 Password Storage History

 

패스워드를 저장하는 표준 매커니즘의 변화

 

🍰 처음에는 일반 텍스트로 암호를 저장했다.

  • 비밀번호를 담고 있는 DB에 접근하려면 credential이 필요했기 때문에 비밀번호가 안전하다고 생각했다.
  • 악의적인 SQL Injection 같은 공격으로 “Data dump”를 읽어갈 수 있는 방법이 존재한다.

 

🍰 암호를 저장하기 전에 SHA-256 같은 단방향 해시를 실행한 후 암호를 저장했다.

  • 사용자가 인증을 시도할 때 입력한 암호를 해시 처리하여 저장된 비밀번호와 입력한 비밀번호의 해시값을 비교한다.
  • 시스템에선 비밀번호의 단방향 해시만 저장하면 됐다.
  • 악의적인 공격으로 정보가 유출이 되더라도 실제 비밀번호가 아닌 비밀번호의 단방향 해시값만 알 수 있다.
  • 단방향 해시값만으로 비밀번호를 추측하기는 불가능에 가까웠다.
  • 하지만 레인보우 테이블로 알려진 룩업 테이블이 만들어지면서 안전하지 않게 됐다.

 

🍰 레인보우 테이블을 무력화하기 위해 솔티드 패스워드(salted password)를 사용했다.

  • 비밀번호 해시 함수 ➡️ 모든 사용자의 비밀번호로 랜덤 바이트(솔트)를 만들었다.
  • 솔트와 사용자의 비밀번호로 해시 함수를 실행하면 유니크한 해시값을 생성한다.
  • 솔트는 유니크하기 때문에 솔트와 비밀번호 조합은 레인보우 테이블을 무력화할 수 있었다.
  • 하지만 기술이 발전함에 따라 최신 하드웨어를 사용하면 초당 수십억 건을 계산할 수 있게 되며 비밀번호를 쉽게 해독할 수 있어 SHA-256같은 해시가 더 이상 안전하지 않게 되었다.

 

🍰 현재는 적응형 단방향 함수(adaptive one-way function)로 비밀번호를 저장하는 것을 권장한다.

  • CPU, 메모리 등 많은 리소스를 소모해서 비밀번호를 검증 ➡️ 애플리케이션 성능이 크게 떨어질 수 있다.
  • 적응형 단방향 함수는 하드웨어 사양에 따라 work factor를 구성할 수 있다.
  • 워크 팩터(work factor) : 공격자가 암호를 깨는데 드는 노력, 시스템에서 비밀번호를 검증할 때 소요되는 시간과 관련있다.
  • 공격자가 쉽게 비밀번호를 해독하지 못하게 만들지만 시스템 자체에 부담이 된다.
  • Spring Security 는 work factor 시작점을 제공하지만 성능에 따라 각자의 시스템에 맞게 설정하는게 좋다.
  • 종류 : bcrypt, PBKDF2, scrypt, argon2
  • 사용하는 부분에서 장기 자격 증명(사용자 이름및 암호)에서 단기 자격 증명(세션, OAuth 토큰 등)으로 바꾸는게 좋다. ➡️ 단기 자격 증명은 동일한 보안 수준을 유지하면서도 빠르게 검증할 수 있다.

 

 


 

 

🍙 Servlet Authentication Architecture

 

 

🍘 Architecture

 

Servlet authentication에서 사용하는 Spring Security의 구성요소

 

🍰 SecurityContextHolder

Spring Security가 인증한 대상에 대한 디테일을 저장하는 곳이다.

SpringContextHolder

 

 

🍰 SecurityContext

SecurityContextHolder에서 가져와진다. 현재 인증된 사용자의 인증을 포함한다.

 

 

🍰 Authentication

AuthenticationManager의 입력으로 사용된다.

사용자 인증을 위해 제공한 credential이나 SecurityContext에 있는 현재 사용자의 credential을 제공한다.

Authentication이 가지고 있는 정보들

  • principal : 사용자를 식별한다. username/password로 인증할 때 주로 UserDetails의 인스턴스를 이용한다.
  • credentials : 일반적으로 password를 사용한다. (유출되지 않도록 인증 후 비운다.)
  • authorities : 사용자에게 부여한 권한은 grantedAuthority로 추상화한다.

 

 

🍰 GrantedAuthority

Authentication에서 principal(주체)에게 부여된 authority(권한)이다. (역할, 범위 등)

Authentication.getAuthorities() 메서드는 GrantedAuthority 객체들을 Collection으로 리턴한다.

권한은 보통 역할(roles)이다. (예 : "ROLE_ADMINISTRATOR", "ROLE_HR_SUPERVISOR")

이런 역할들은 나중에 웹 권한 부여, 메서드 권한 부여 및 도메인 객체 권한 부여를 위해 구성된다.

username/password 기반 인증을 사용한다면 UserDetailsServiceGrantedAuthority를 로드한다.

GrantedAuthority 객체는 애플리케이션 전체에 걸친 권한을 의미한다. (특정 도메인으로 한정하는 목적으로 사용하진 않는다.)

 

 

🍰 AuthenticationManager

Spring Seuciry의 필터가 어떻게 인증을 수행하는지에 대한 방법을 정의하는 API이다.

  • AuthenticationManager를 호출한 객체(Filter)가 리턴한 AuthenticationSecurityContextHolder에 설정한다.
  • Spring Security의 Filters를 사용하지 않을 경우 AuthenticationManager를 사용하지 않고 SecurityContextHolder에 직접 설정하면 된다.
  • AuthenticationManager의 가장 많이 사용하는 구현체는 ProviderManager이다.

 

 

🍰 ProviderManager

AuthenticationManager의 가장 일반적인 구현체이다.

  • ProviderManager는 동작을 AuthenticationProvider List에 위임한다.
  • 모든 AuthenticationProvider는 인증을 성공 or 실패 or 결정을 내릴 수 없는 지를 판단하고 다운스트림에 있는 AuthenticationProvider가 결정하게 할 수 있다.
  • 설정한 AuthenticationProvider들 중 어떤 것도 인증할 수 없다면, ProviderNotFoundException 오류와 함께 인증이 실패한다.
  • ProviderNotFoundException : ProviderManager가 전달된 Authentication 유형을 지원하도록 구성되지 않았음을 나타낸다.
  • AuthenticationProvider마다 각자 맡은 인증을 수행한다.

 

ProviderManager

 

  • ProviderManager에 인증을 수행할 AuthenticatinoProvider가 없을 경우, 부모 AuthenticationManager 구성을 허용한다.
  • 부모는 어떤 타입의 AuthenticationManager든 가능하지만 주로 ProviderManager의 인스턴스이다.

 

ProviderManager - AuthenticationManager

 

  • 여러 개의 SecurityFilterChain 인스턴스가 공통의 인증(같은 부모 AuthenticationManager)을 가지지만 다른 인증 매커니즘(다른 ProviderManager 인스턴스)일 경우 같은 부모 AuthenticationManager를 여러 ProviderManager가 공유할 수 있다.

 

multiple ProviderManager

 

  • ProviderManager는 인증 이후의 리턴된 Authentication 객체의 모든 민감한 credential 정보는 제거하려고 시도한다.
  • password와 같은 정보가 HttpSession에 필요 이상으로 오래 남아있는 것을 방지하기 위해서이다.
  • 하지만 Authentication에 cache의 객체(ex. UserDetails)에 대한 참조가 포함되어 있는 경우 credential 정보를 모두 제거하면 캐시된 값에 대해 더 이상 인증할 수 없는 문제가 생길 수 있다.
  • 해결책은 캐시 구현 또는 반환된 Authentication 객체를 생성하는 AuthenticationProvider에서 먼저 객체의 복사본을 만드는 것이다.
  • 또 다른 해결책으로는 ProviderManagereraseCredentialsAfterAuthentication property를 비활성화 하는것이다.

 

 

🍰 AuthenticationProvider

다수의 AuthenticationProviderProviderManager에 주입할 수 있다.

각각의 AuthenticationProvider는 특정 유형의 인증을 수행한다.

ex) DaoAuthenticationProvider : username/password 기반의 인증을 지원한다.

JwtAuthenticationProvider : JWT 토큰 인증을 지원한다.

 

 

🍰 Request Credentials with AuthenticationEntryPoint

AuthenticationEntryPoint는 클라이언트의 자격증명 요청에 대한 응답 HTTP를 보내는데 사용된다.

또한 클라이언트에 자격증명을 요청할 때 사용된다.

AuthenticationEntryPoint의 구현체는 로그인 페이지로 리디렉션하거나 WWW-Authenticate header를 전송하는 것을 수행한다.

 

 

🍰 AbstractAuthenticationProcessingFilter

사용자의 credentials를 인증하기 위한 기초 Filter로 사용된다.

credentials이 인증되기 전에, Spring Security는 일반적으로 AuthenticationEntryPoint를 사용하여 credentials을 요청한다.

여러 component를 조합하여 심도있는 인증 플로우를 구성할 수 있다.

 

 

 

 

참고) Servlet Authentication Architecture

참고) Spring Security doc

 

 

 

감사합니다.

오개념에 대한 지적은 늘 환영입니다. 😃

 

 

 

1 2 3 4 5 6 ··· 11