오늘 공부한 내용

  • 코드카타 2문제
  • 면접대비 2문항 공부
  • 마지막 프로젝트 제작 / 탈퇴 기능 수정 및 코드 정리

 

새로 알게된점

  • TCP/UDP에 대해서 설명

TCP : 인터넷상에서 데이터를 메세지의 형태로 보내기 위해 IP와 함께 사용하는 프로토콜

  1. 연결형 서비스 패킷 교환 방식을 사용한다.
  2. 3-way handshaking과정을 통해 연결을 설정하고 4-way handshaking을 통해 해제한다.
  3. 흐름 제어 및 혼잡 제어(데이터 처리속도를 조절, 수신자의 버퍼 오버플로우 방지)
  4. 높은 신뢰성을 보장한다.
  5. UDP보다 속도가 느리다.

UDP : 데이터를 데이터그램 단위로 처리하는 프로토콜

  1. 비연결형 서비스로 데이터그램 방식을 제공한다
  2. 정보를 주고 받을 때 정보를 보내거나 받는다는 신호절차를 거치지 않는다.
  3. UDP헤더의 CheckSum 필드를 통해 최소한의 오류만 검출한다.
  4. 신뢰성이 낮다
  5. TCP보다 속도가 빠르다

TCP와 UDP의 신뢰성 차이는 TCP의 연결 설정과 종료, 에러 검출 및 재전송 등의 과정이 있으며, 반면에 UDP는 이러한 과정이 없기 때문에 발생한다.

 

* 3-way handshaking은 TCP에서 신뢰성 있는 통신을 위해 연결을 설정하는 과정

1. 클라이언트가 서버에게 연결 요청과 함께 SYN 패킷을 보냄

2. 서버는 이를 받고, SYN-ACK 패킷을 서버 시퀸스번호와 함께 클라이언트에게 보냄

3. 클라이언트는 서버의 SYN-ACK 패킷을 받고 이를 확인하는 ACK 패킷을 보냄

 

 

  • http, https 차이점

HTTP : 클라이언트와 서버 간 통신을 위한 통신 규칙

HTTPS : HTTP확장판, 브라우저와 서버가 데이터를 전송하기 전에 안전하고 암호화된 연결을 설정

 

차이점

보안 : 가장 큰 차이점은 보안입니다. HTTP는 암호화되지 않은 텍스트로 데이터를 전송하는데 반해, HTTPSSSL 또는 TLS를 이용하여 모든 통신을 암호화합니다. 이로 인해 중간에서 데이터를 가로채더라도 그 내용을 이해하는 것이 어렵습니다.

포트 : HTTPHTTPS는 서버와 클라이언트 간의 통신을 위해 다른 포트를 사용합니다. HTTP80번 포트를, HTTPS443번 포트를 사용합니다.

URL 표시 : 대부분의 웹 브라우저에서 HTTPS 사이트의 URL 앞에 '보안'을 의미하는 자물쇠 아이콘이 표시됩니다. 이는 사용자에게 이 사이트가 안전하다는 신호를 보내주는 역할을 합니다.

인증 : HTTPS는 인증서를 사용해 서버의 신원을 검증합니다. 이 인증서는 신뢰할 수 있는 제3자 기관(Certificate Authority, CA)에 의해 발급되며, 사용자는 이를 통해 자신이 연결하려는 사이트가 실제로 해당 사이트임을 확인할 수 있습니다.

 

 

오늘 공부한 내용

  • 코드카타 2문제
  • 면접대비 2문항 공부
  • 마지막 프로젝트 제작 (하이브유저 가입, 전체검색, 단일검색, 탈퇴  구현 완료)

 

어려웠던 내용

  • 코드 작성시 큰 문제없이 잘 작성이 잘된거 같아서 실행에 문제는 없었는데 Postman 으로 적용시 쿼리문이 나가지만 실질적인 데이터베이스에는 적용이 안되어서 찾아보니 @Transactional이 안적혀 있거나 혹은 Postman에서 / 하나가 더적혀 있는 등 미세한 문제점을 찾아내는데 시간을 많이 사용하게 되어서 어려웠다.

 

새로 알게된점

  • 브라우저의 작동방식에 대해서 설명해주세요.

정의 : URL을 입력하면 웹 서버라 불리는 프로그램이 웹 브라우저에 웹 페이지를 제공

 

작동방식

  1. 사용자가 URL을 입력하면 브라우저는 웹 서버에 HTTP 요청을 보내고 데이터를 수신한다.
  2. 받은 HTMLCSS는 파싱되어 DOMCSSOM 트리를 생성하며, 이들은 렌더 트리로 병합된다.
  3. 렌더 트리를 기반으로 레이아웃 단계에서 각 요소의 크기와 위치를 계산한다.
  4. 계산된 정보로 화면에 페이지를 그리는 페인팅 단계가 이루어진다.
  5. 필요한 JavaScript가 실행되고 모든 요소가 로드되면 페이지가 완성되어 사용자와 상호 작용한다.

 

 

  • 쿠키, 세션의 개념과 차이를 설명해보세요

쿠키 : HTTP의 일종으로 사용자가 웹 사이트를 방문할 경우 사용되는 사용자의 컴퓨터에 저장하는 작은 기록 정보 파일

세션 : 방문자가 웹 서버에 접속한 시점부터 웹 브라우저를 종료하여 연결을 끝내는 시점, 즉 서버에 접속해 있는 상태

 

차이점

  1. 사용자의 정보가 저장되는 위치이며, 쿠키는 서버의 자원을 전혀 사용하지 않지만 세션은 서버의 자원을 사용
  2. 쿠키는 만료기간이 파일로 저장되어 브라우저를 종료해도 정보가 유지되지만, 세션은 브라우저가 종료되면 만료시간에 상관없이 삭제됨으로 라이프 사이클에 큰 차이점을 가지고 있다.
  3. 이러한 특성으로 보안 측면에서 세션이 더 우수하나 요청속도는 쿠키가 더 빠르다.

쿠키 사용이유세션은 서버의 자원을 사용하기 때문에 무분별하게 만들다보면 서버의 메모리가 감당할 수 없어질 수가 있고 속도가 느려질 수 있기 때문에 쿠키가 유리한 경우가 있다

 

오늘의 느낀점

코드 작성시 중간 테이블에서 값을 찾기위하여 repository에서 메서드를 설정하는게 어려워서 작성에 많은 어려움을 가지다 보니 팀원의 도움으로 추천 메서드가 아닌 @Quary 어노테이션을 통해서 jpa에 있는 데이터를 직접적으로 적용하는 방식을 사용했다.

처음 사용할 시에는 오히려 낮설었지만 오히려 기존 repository에서 추천으로 작성하는것보다 좀더 상세하게 자료를 가져올수 있다보니 편리함을 느꼈다.

좀더 연습해서 익숙해지면 앞으로 매개변수의 값을 찾는데는 문제없이 코드 작성이 가능 할것이라고 생각된다.

오늘 공부한 내용

  • 코드카타 2문제
  • 면접대비 2문항 공부
  • 마지막 프로젝트 제작 (하이브 전체조회, 단일조회, 삭제 기능 구현 완료)

 

어려웠던 내용

  • 코드 작성시 큰 문제없이 잘 작성이 잘된거 같아서 실행에 문제는 없었는데 Postman 으로 적용시 쿼리문이 나가지만 실질적인 데이터베이스에는 적용이 안되어서 찾아보니 @Transactional이 안적혀 있거나 혹은 Postman에서 / 하나가 더적혀 있는 등 미세한 문제점을 찾아내는데 시간을 많이 사용하게 되어서 어려웠다.

 

새로 알게된점

  • HTTP 메서드

주요 메서드

GET : 리소스 조회 (서버에 전달하고 싶은 데이터는 query을 통해서 전달)

POST : 등록, 요청 데이터 처리 (데이터 전송시 Body에 담아 전송하므로 메시지 길이 제한이 없음)

PUT : 리소스 덮어쓰기(전체 변경, 기존데이터 A,BC를 보낼시 A,B삭제후 C로 대체됨.)

PATCH : 리소스 부분 변경(일부 변경, 기존데이터 A,BB=C로 대체시 A,C로 대체됨)

DELETE : 리소스 삭제

 

기타 메서드

HEAD : GET과 동일하나 메시지 부분(Body)를 제외하고, 상태 줄과 헤더만 반환

OPTIONS : 대상 리소스에 대한 통신 가능 옵션(메서드)을 설명

CONNECT : 대상 자원으로 식별되는 서버에 대한 터널을 설정

TRACE : 대상 리소스에 대한 경로를 따라 메시지 루프백 테스트를 수행

 

  • CORS(교차 출처 리소스 공유)

CORS(교차 출처 리소스 공유)

- 추가 HTTP헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을부여하도록 브라우저에 알려주는 체제

 

CORS 필요 이유

- CORS가 없이 모든 곳에서 데이터를 요청할 수 있게 된다면, 다른 사이트에서 원래 사이트를 흉내낼 수 있게 된다. 이를 통해 로그인했던 세션 또는 토큰을 탈취하여 정보를 꺼내는 등 해킹이 가능해지므로 브라우저를 보호하고 필요한 경우에만 서버를 요청하기위하여 필요하다.

 

SOP(동일 출처 정책)

같은 출처에서만 리소스를 공유할 수 있다는 규칙을 가진 정책

다른 출처로부터 조회된 자원들의 읽기 접근을 막아 다른 출처 공격을 예방

 

오늘의 느낀점

코드 작성시 어렵게 느껴지는 부분은 작성후 확인했을때 오류를 찾아내는 점이 제일어렵게 느껴진다.

다만 코드 자체에서 문제일시 오류부분을 보면 찾는데 큰 어려움없이 문제 부분을 찾아서 해결책을 구상하지만 코드상 문제가 없다고 생각했는데 내가 원하는 값이 나오지 않아서 이에 문제점을 찾는 과정이 제일어렵다.

대부분 이럴경우 내가 철자를 잘못 입력하거나 미세한 실수로 일어나다 보니 더욱 찾기 어렵게 느껴지는거 같다.

작성시 코드 글자를 하나씩 차분히 뜯어보는 습관을 좀더 들여야 할거 같다.

오늘 공부한 내용

  • 코드카타 2문제
  • 면접대비 2문항 공부
  • 마지막 프로젝트 제작 (하이브 생성, 수정 기능 구현 완료)

 

어려웠던 내용

  • 하이브 생성 기능 구현시 매개변수 작성에서 혼란을 겪었으나 팀원의 도움을 받아 작성후 기능 구현에 완료 하였습니다.

 

새로 알게된점

  • RDBMS의 정규화

정규화 정의 : 관계형 데이터베이스(RDBMS)의 설계에서 데이터를 중복 없이 효율적으로 저장하기 위한 과정,

이를 통해 데이터 베이스의 불필요한 중복을 최소화하고 데이터 일관성과 무결성을 유지 가능함.

 

 1 정규형

- 테이블의 컬럼이 원자값(Atomic Value, 하나의 값)을 갖도록 테이블을 분해하는 것

 

 2 정규형

- 정규화를 진행한 테이블에 대해 완전 함수 종속 만족하도록 테이블을 분해하는 

- 완전함수종속 : 기본키가 구성하는 모든 속성에 종속되는 경우

- 테이블에서 기본키가 복합키(키1, 키2)로 묶여있을 때, 두 키 중 하나의 키만으로 다른 컬럼을 결정지을 수 있으면 안된다.

 

 3 정규형

- 정규화를 진행한 테이블에 대해 이행적 종속 없애도록 테이블을 분해하는 

- 이행적 종속 : A → B, B → C면 A → C가 성립된다

 

BCNF 정규형

3 정규화를 진행한 테이블'에 대해 모든 결정자가 후보키가 되도록 테이블을 분해하는 것

각 행(튜플, 가로줄)을 유일하게 식별하기 위해 사용하는 부분집합

 

  • Primary key, Foreign key 란

 : 관계(테이블)에서 행(튜플)을 식별하는 데 도움이 되는 속성

기본키 : 테이블의 모든 행을 고유하게 식별하는 열 또는 열 그룹

- null을 허용하지 않음, 유니크한 값

- 사용이유 : 기록 삭제, 수정 시 데이터 무결성을 확인하기 위함.

외래키 : 다른 테이블과의 관계를 맺기 위해 사용

- 여러개의 null값을 허용,

- 사용이유 : 두 개 이상의 테이블을 함께 연결, 데이터의 무결성과 일관성을 보장

 

객체 관계

1:1 = 한 테이블의 레코드가 다른 테이블의 레코드와 1:1로 대응, 한 명의 사용자가 한 개의 주소를 가지는 경우

단방향 : 외래 키 주인만이 외래 키 를 등록, 수정, 삭제할 수 있으며주인이 아닌 쪽은 오직 외래 키를 읽기만 가능

양방향 : 외래 키의 주인을 지정해 줄 때 mappedBy 옵션을 사용

 

1:N = 한 테이블의 레코드가 다른 테이블의 레코드와 1:N으로 대응, 한 명의 사용자가 여러 개의 주문을 가지는 경우

- N쪽에 외래키 성성, 1쪽이 연관 관계의 주인이 됨.

- 실무에서는 잘 사용하지 않으며 그 이유는 RDB 관점에서는 다수(Many)의 임장이 FK를 가져야 올바른 설계 이므로

 

N:1 = 1:N 관계의 반대에 해당 하는 경우, 여러 명의 선수가 한팀에 소속되는 경우

- N쪽에 외래키 생성, 연관 관계의 주인이 됨

 

N:M = 한 테이블의 레코드가 다른 테이블의 레코드와 N:M으로 대응, 여러 명의 학생이 여러 개의 과목을 수강하는 경우

관계형 데이터베이스는 정규화된 테이블 2개로 표현불가, 따로 테이블 생성

 

단점 

- 중간 테이블에는 매핑정보만 들어가고 추가 데이터를 넣는 것이 불가능,

- 중간 테이블이 숨겨져 있어 쿼리가 예상못한 형태로 출현

 

 

오늘의 느낀점

기술 면접을 하루하루 준비하고 공부하면서 내가 배웠던 지식에대해 다시 한번 재정립하고 더 많은 내용을 배울수 있게 되었다.

또한 스파르타 에 온이후 내가 스스로 처음으로 프로젝트에서 CRUD에서 생성과 수정을 큰 문제 없이 작성 할 수 있었다.

이전에 있던 팀에서 의 작성방법을 많이 타긴 했지만 앞으로 다양한 프로젝트를 겪으면서 더 많은 방식을 익히면 어떤 상황이라도 바로바로 적용할수 있는 능력을 기르게 될것이라고 생각한다.

 

오늘 공부한 내용

  • 코드카타 2문제
  • 면접대비 2문항 공부
  • 마지막 프로젝트 주제 구현 및 SA추가 작성

 

어려웠던 내용

  • 하이브 생성 기능 구현시 매개변수 작성에서 혼란을 겪었으나 팀원의 도움을 받아 작성후 기능 구현에 완료 하였습니다.

 

새로 알게된점

  • NoSQLRDBMS의 특징과 차이점에 대한 장, 단점

RDBMS : `관계형 데이터베이스` `Schema` `Table` `SQL`

정의 : 관계형 데이터베이스를 생성하고 수정, 삭제 관리할 수 있는 소프트웨어

특징 : 데이터 구조가 변경될 여지 없이 명확, Update가 잦은 시스템, 외래키를 이용한 테이블 간 Join 가능

장점 : DataColumn, Row형태로 저장, 분류, 정렬, 탐색속도 빠름, 스키마로 명확한 데이터구조 보장

단점 : 시스템이 커지면 Join문이 많은 복잡한 쿼리 생성, 수직적 확장을 주로 사용

 

NoSQL : `대용량데이터` `비규격화` `Key,Value`

정의 :

특징 : 데이터양이 많으며 Update가 자주 이루어지지 않을 때 사용, Key.Value 사용시 이미지, 비디오 등 어떠한 형태의 데이터도 담을 수 있다.

장점 : 유연한 데이터 구조, 수평적 확장 용이

단점 : 데이터 중복 발생 가능, 데이터 변경시 모든 컬렉션 수정필요,

 

  • mvc 패턴

- 답변 `model` `view` `controller`

정의 : 프로젝트 구성할 때 그 구성 요소를 세가지 역할로 구분한 패턴

장점 : 비교적 간단한 패턴으로 구조파악과 확장을 쉽게 할 수 있다.

단점 : 뷰와 모델의 완벽한 분리가 어렵고 앱이 커지면 컨트롤러의 코드량이 커져 유지보수 하기가 힘들다.

 

model (모델)

- 컨트롤러가 호출을 하면 DB와 연동하여 사용자의 입출력 데이터를 다루는 일과 같은 데이터와 연관된 비즈니스 로직을 처리하는 역할

- 데이터 추출, 저장, 삭제, 업데이트 등의 역할을 수행

 

view ()

- 사용자와 상호작용을 하며 컨트롤러로부터 받은 모델의 결과값을 사용자에게 화면으로 출력

 

controller (컨트롤러)

- ModelView 사이를 이어주는 인터페이스 역할

- Model이 데이터를 어떻게 처리할지 알려주는 역할

 

 

오늘 공부한 내용

  • 코드카타 2문제
  • Spring 심화 프로젝트 발제
  • SA 작성 / API구상, ERD구상, 와이어프레임 작성, Entity작성

https://teamsparta.notion.site/HHive-56f9924035a34b94ae3b5e4ec4d1421a

금일 팀원들과 만든 SA이다.

오늘의 느낀점

드디어 마지막 프로젝트에 들어가게 되었다.

이번 프로젝트의 주제로는 이전에 만들었던 Trello를 바탕으로 서로 간의 취미를 공유해서 만남을 가지는 어플리케이션을 만드는 것이 목표이다.

저번에 확실히 많이 배우긴 했다만 이번에 만드는 부분을 보니 어려워 보이는 부분도 많아서 시간이 많이 걸릴거 같다.

마지막 프로젝트인 만큼 힘내서 잘하고 싶다.

KPT 회고

Keep

  • 원활한 일정공유와 의사소통
  • error를 계속 공유해서 함께 문제 해결

Problem

  • 주말이 필요한 팀원들
  • 부족한 실력....

Try

  • cd 까지 구축
  • 아키텍쳐 작성
  • test code 작성
  • 발표자료 작성 시, 자세한 설명 부족 → 피드백 반영해야함

한마디

이지선

충분한 의사소통과 빠른 의사결정으로, 기획부터 개발까지 문제 되는 부분 없이 매끄럽게 진행됐습니다. 각자 하기로 한 부분은 어떻게든 끝내는 모습이 인상 깊었습니다. 결과도 좋고 팀웍도 좋았어서 깔끔하게 마무리해서 너~~~무 좋았습니다

 

김종규

원활한 의사소통과 문제 발견 시 공유해서 해결하는 부분이 팀의 긍정적인 시너지를 많이 느끼게 되었습니다. 이번 프로젝트 수고많으셨고, 최종 프로젝트까지 다들 화이팅하시길 기원합니다.

김대영

젭 뿐만아닌 부재시 슬랙을 이용하여 빠른 정보전달을 통해서 빠른 문제 해결, 코드 작성으로 프로젝트 작업간 효율성을 극대화하여 높은 완성도를 이루었던거 같습니다. 또 이번 프로젝트에서도 새로운 기능과 프로그램에대해 알고 배울수 있어서 좋은 경험이 되었습니다.

김혜윤

역대 원할한 소통이 가장 잘 된 팀으로 문제 해결 및 개발 진행에서 집단 지성을 잘 사용한 것 같습니다. 피드백을 통해 트러블 슈팅 작성 시, 문제 상황을 더 자세하게 작성 해야겠습니다. 순서 변경 로직을 프론트 없이 백엔드에서 좀 더 효율성(?)있게 구현 할 수 있는 방법을 찾아보는 것이 숙제…! 다들 수고하셨습니다.!

오늘 공부한 내용

  • 코드카타 2문제
  • Spring 심화 프로젝트 작성

 

새로 알게된점

Swagger - UI 란?

  • 애플리케이션의 RESTful API 문서를 자동으로 구성하는 특수 도구를 말한다.

 

 프로젝트 내에서 지정한 url들을 아래와 같이 자동으로 문서화해준다.

이곳에서 간단히 api를 볼 수 있을 뿐만 아니라, 요청과 응답 테스트 또한 진행할 수 있다.

런 점들이 백엔드와 프론트엔드 간의 협업과 소통을 돕는다.

 

 

Swagger - UI를 사용하기 위한 설정

build.gradle 추가 설정

의존성에 springdoc-openapi-ui를 추가한다.

dependencies {
// spring doc
implementation group: 'org.springdoc', name: 'springdoc-openapi-starter-webmvc-ui', version: '2.3.0'
}

 

 

Config 파일 구성

package com.sparta.missionreport.global.config;

import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
import io.swagger.v3.oas.annotations.info.Info;
import io.swagger.v3.oas.annotations.security.SecurityScheme;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@OpenAPIDefinition(info = @Info(title = "Mission Log App",
	description = "Mission Log App Api 명세서",
	version = "v1"))
@SecurityScheme(name = "Bearer Authentication",
	type = SecuritySchemeType.HTTP,
	bearerFormat = "JWT",
	scheme = "Bearer")
@Configuration
public class SwaggerConfig {

    @Bean
    public OpenAPI openAPI() {
        return new OpenAPI()
                .components(new Components());
    }
}

@Configuration 어노테이션을 통해서 OpenAPI 관련 문서를 생성하며,

@OpenAPIDefinition, @SecurityScheme 어노테이션을 SwaggerConfig에 적용하면, 위의 페이지의 Authorize버튼을 구현할 수 있다.

 

이 버튼은

회원정보로 발급받은 토큰을 입력하면 그 시점부터 swagger-ui에서 테스트로 보내는 요청은 모두 그 회원의 권한으로 보낼 수 있다.
로그인되지 않은 사용자, 로그인한 회원, 관리자 이러한 종류의 회원 권한들로 다양하게 요청 테스트를 진행할 수 있다.

 

.properties 파일 설정

# swagger
spring.mvc.pathmatch.matching-strategy=ant_path_matcher

# spring doc
springdoc.swagger-ui.path=/swagger-ui.html
springdoc.swagger-ui.tags-sorter=alpha
springdoc.show-login-endpoint=true

객체를 생성할 수 있는 빌더를 builder() 함수를 통해 얻고 거기에 셋팅하고자 하는 값을 셋팅하고 마지막에 build()를 통해 빌더를 작동 시켜 객체를 생성한다.

 

controller 설정

@Slf4j
@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
@Tag(name = "2. Board Controller", description = "Board Controller")
@SecurityRequirement(name = "Bearer Authentication")
public class BoardController {

    private final BoardService boardService;

    @Operation(summary = "보드 생성")
    @PostMapping("/boards")
    public ResponseEntity<CommonResponseDto> createboard(
            @AuthenticationPrincipal UserDetailsImpl userDetails,
            @RequestBody @Valid BoardDto.CreateBoardRequest createBoardRequest) {
        BoardDto.Response response = boardService.createBoard(userDetails.getUser(),
                createBoardRequest);
        return ResponseEntity.status(
                        HttpStatus.CREATED)
                .body(new CommonResponseDto(HttpStatus.CREATED.value(), "보드가 작성되었습니다.", response));
    }

위의 예시는 익명게시판에서 보드를 생성하기위한 컨트롤러 의 Create 부분 이며 

Controller의 상단에 @Tag 어노테이션과 @Operation 어노테이션을 추가한다.

 

Swagger 명세서 작성

@Tag / api 그룹 설정을 위한 어노테이션

name 속성으로 태그의 이름을 설정할 수 있고, description 속성으로 태그에 대한 설명을 추가할 수 있다.

@Tag에 설정된 name이 같은 것 끼리 하나의 api 그룹으로 묶게 된다.

 

@Operation / api 상세 정보 설정을 위한 어노테이션

summary 속성으로 api에 대한 간략한 설명, description 속성으로 api에 대한 상세 설명을 추가할 수 있으며, responses, parameters 속성 등을 추가로 적용할 수 있다.

 

Request, Response 객체 설정

public class BoardDto {

    @Getter
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    @Schema(description = "보드 생성 요청 dto")
    public static class CreateBoardRequest {

        @NotBlank
        @Schema(description = "보드 이름", defaultValue = "개발 전체 진행 완료하자")
        private String name;

        public Board toEntity(User createdBy) {
            return Board.builder().name(name).createdBy(createdBy).build();
        }
    }

    @Getter
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    @Schema(description = "보드 수정 요청 dto")
    public static class UpdateBoardRequest {

        @Schema(description = "보드 이름", defaultValue = "보드 이름 수정 test")
        private String name;

        @Schema(description = "보드 색상", defaultValue = "blue")
        private Color color;

        @Schema(description = "보드 설명", defaultValue = "보드, 수정 완료하기")
        private String description;

        @Schema(description = "보드 마감 일자", defaultValue = "2023-01-03 09:00")
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm")
        private LocalDateTime deadLine;
    }

    @Getter
    @Builder
    @NoArgsConstructor
    @AllArgsConstructor
    @Schema(description = "보드 정보 반환 dto")
    public static class Response {

        @Schema(description = "보드 id", defaultValue = "1L")
        private Long id;

        @Schema(description = "보드 이름", defaultValue = "제발...문제없이 작동해주세요")
        private String name;

        @Schema(description = "보드 설명", defaultValue = "카드, 댓글 기능 구현 완료하기")
        private String description;

        @Schema(description = "카드 색상", example = "red")
        private String color;

        @Schema(description = "생성 유저 이메일", defaultValue = "sparta@gmail.com")
        private String createdBy;

        @Schema(description = "보드 작업자 리스트", defaultValue = "[\"spsta@gmail.com\", \"worker1@gmail.com\",\"worker2@gmail.com\"]")
        private List<String> boardWorkers;

        @Schema(description = "보드 생성 일자", defaultValue = "2023-01-01T00:00:00")
        private LocalDateTime createdAt;

        public static BoardDto.Response of(Board board) {
            return Response.builder()
                    .id(board.getId())
                    .name(board.getName())
                    .description(board.getDescription())
                    .color(board.getColor().getColor())
                    .createdBy(board.getCreatedBy().getEmail())
                    .boardWorkers(board.getBoardWorkerList().stream().map(BoardWorker::getUserEmail)
                            .toList())
                    .createdAt(board.getCreatedAt())
                    .build();
        }
    }
}

위의 예시처럼 @Schema 를 이용하여 각부분의 설명, 기본값을 설정하여 기입할수 있다.

 

@Schema / Request, Response 객체에 대한 설정을 위한 어노테이션

description으로 설명을 추가할 수 있고, defaultValue으로 기본적으로 사용할 값을 설정할 수 있으며, example을 통해 예시를 설정할 수도 있다.

 

느낀점

Postman이 아닌 Swagger-UI 라는 새로운 API 테스트 프로그램을 프로젝트 하면서 배우게 되었다.

혼자 작성해서 확인해야하는 Postman에 비해 Swagger-UI는 코드 작성시 팀원들과 토의로 일정 규칙하에 작성하면 기본값을 설정한상태로 테스트가 가능하기에 좀더 효율적으로 시행할수 있다.

프로젝트를 하면서 팀원들에게 새로운 것을 배울때 마다 감사를 느끼며 더욱더 열심히 해야겠다고 생각한다.

오늘 공부한 내용

  • 코드카타 2문제
  • Spring 심화 프로젝트 작성

 

새로 알게된점

@Builder

빌더 패턴 (Builder patter) 이란?

 

복잡한 Object들을 단계별로 구축할 수 있는 생성 디자인 패턴을 말하며 이 패턴을 사용하면, 동일한 구성코드를 사용하여 다양한 타입과 표현을 제공한다.

 

일반적으로 객체를 정의하고 그 객체를 생성할 때 보통 생성자를 통해 생성하는 것을 생각한다.

Bag bag = new Bag("name", 1000, "memo");

 

 

하지만 생성자를 통해 객체를 생성하는데 몇 가지 단점이 있기에 빌더패턴으로 작성시 이를 보완할수 있다.

Bag bag = Bag.builder()
		.name("name")
        	.money(1000)
        	.memo("memo")
        	.build();

 

객체를 생성할 수 있는 빌더를 builder() 함수를 통해 얻고 거기에 셋팅하고자 하는 값을 셋팅하고 마지막에 build()를 통해 빌더를 작동 시켜 객체를 생성한다.

 

빌더를 사용하는 이유

  •  뛰어난 가독성을 가지고 있다.

생성자 파라미터가 많을 경우 가독성이 좋지 않다.

예시로 위에 작성했던 생성자 파라미터가 3개가 아닌 7~8개가 되면 어떻게 될까?

Bag bag = new Bag("name", 1000, "memo", "abc", "what", "is", "it", "?");

 이렇게 작성될시 각 값들이 어떤 값을 의미하는지 이해하기 힘들 것이다.

 

하지만 빌더 패턴으로 구현하면 각 값들이 빌더의 각 값들의 이름 함수로 셋팅되어 각각 무슨 값을 의미하는기 파악하기 쉬워지며 가독성이 높아지고 추후 같은 타입의 다른 변수의 값을 서로 바꿔 넣는 것을 방지 할 수도 있다

Bag bag = Bag.builder()
		.name("name")
        	.money(1000)
        	.memo("memo")
            	.letter("This is the letter")
            	.box("This is the box")
        	.build();

 

  •  데이터의 순서에 상관없이 객체 성성이 가능하다.

생성자의 경우는 정해진 파라미터 순서대로 꼭 값을 넣어줘야한다. 순서를 무시하고 값을 넣으면 에러가 발생하거나 엉뚱한데 값이 들어갈 수 있다.

public Bag(String name, int money, String memo) {
	this.name = name;
    	this.money = money;
    	this.memo = memo;
}

 

하지만 빌더 패턴은 빌더의 필드 이름으로 값을 설정하기 때문에 순서에 종속적이지 않다.

그냥 쓰이는 곳에서 어떤 필드를 먼저 설정해야하는지 굳이 순서를 생각할 필요 없이 편하게 설정하면 된다.

Bag bag = Bag.builder()
		.name("name")
        	.memo("memo")	// memo를 money 대신 먼저!
        	.money(1000)
        	.build();

 

추가적인 장점으로

  • 불필요한 생성자의 제거
  • setter 메서드가 없으므로 변경 불가능한 객체를 만들수 있다. (객체불변성)
  • 한번에 객체를 생성하므로 객체 일관성이 깨지지 않는다.

 

빌더는 사용하는 방법으로 다음과 같이 만들어진다.

- Member 클래스 내부에 빌더클래스 생성.

- 각 멤버 변수별 메서드를 작성, 각 메소드는 변수에 값을 set하고 빌더객체를 리턴한다.

- build() 메서드는 필수 멤버변수의 null체크를 하고 지금까지 set된 builder를 바탕으로 Member 클래스의 생성자를 호출하고 인스턴스를 리턴.

public class Member {

    private String nickname; //필수
    private String password; //필수
    private Gender gender; //선택

    public static class Builder {
        private final String nickname;
        private final String password;
        private Gender gender;

        // 필수변수는 생성자로 값을 넣는다.
        public Builder(String nickname, String password) {
            this.nickname = nickname;
            this.password = password;
        }

        // 멤버변수별 메소드: 빌더클래스의 필드값을 set하고 빌더객체를 리턴한다.
        public Builder gender(Gender gender) {
            this.gender = gender;
            return this;
        }

        // 빌더메소드
        public Member build() {
            return new Member(this);
        }
    }

    public Member(Builder builder) {
        this.nickname = builder.nickname;
        this.password = builder.password;
        this.gender = builder.gender;
    }
}

Member memberEntity = new Builder("minji", "1234")
                .gender(Gender.F)
                .build();

하지만 빌더 패턴도 역시 단점이 존재한다. 객체를 생성하려면 우선 빌더객체를 생성해야 하고, 보다시피 다른 패턴들보다 많은 코드를 요구하기 때문에 인자가 충분히 많은 상황에서 이용할 필요가 있다.

 

아니면 @Builder 을 이용하여 빌더클래스를 직접 만들지 않고 어노테이션 하나로 클래스를 생성할 수 있다.

클래스 또는 생성자 위에 @Builder 어노테이션을 붙여주면 빌더패턴 코드가 빌드된다.

생성자 상단에 선언시 생성자에 포함된 필드만 빌더에 포함된다.

  • 클래스 전체 Builder 적용
@Getter @Builder // ✨ 클래스 전체 필드를 빌더로 사용 가능!
public class UserLombok {

  private Long id;
  private String email;
  private String password;
  private String name;
}

// 사용예제
public User join(String email, String password, String name) {
  UserLombok build = UserLombok.builder()
            .email(email)
            .password(password)
            .name(name)
            .build();
  ...
}
 
 
  • 특정 생성자에서만 Builder 적용 가능
@Getter
public class UserLombok {

  private Long id;
  private String email;
  private String password;
  private String name;
  
  public UserLombok(Long id, String email, String password, String name) {
    this.id = id;
    this.email = email;
    this.password = password;
    this.name = name;
  }

  @Builder // ✨ 빌더는 email, password만 사용 가능
  public UserLombok(String email, String password) {
    this.email = email;
    this.password = password;
  }
}

// 사용예제 - email, password만 가능!
public User join(String email, String password, String name) {
  UserLombok build = UserLombok.builder()
            .email(email)
            .password(password)
            .build();
  ...
}
 

 

 

오늘 공부한 내용

  • 코드카타 2문제
  • Spring 심화 프로젝트 코드작성

 

 

 

오늘의 느낀점

프로젝트의 각자 작성해야하는 코드에 대해 팀원들과 토의를 진행하였는데 API 세분화를 위하여 크게 도메인, 글로벌 2가지 형태로 패키지를 나눈후 도메인안에는 각자 맡아서 작성해야하는 API에대해서 Controller - Service - Repository - Dto - Entity  형태로 패키지를 정리하고, 글로벌 패키지쪽에는 로그인 및 JWT,Config, CommonResponse에 관한 내용으로 구분하여 정리하니 확실히 각 클래쓰에 대한 파일 정리와 구분이 명확해 졌다.

앞으로도 프로젝트 작성시 이러한 형태를 먼저 구상후 시행하면 좀더 빠르고 수월하게 프로젝트를 만들수 있을거 같다.

+ Recent posts