728x90
1. 이번주 궁금증들
(1) 기능 구현 후 테스트 코드를 작성하는 것까지는 알겠어. 그런데 내 테스트 코드가 정말 좋은 테스트 코드일까?
- 양재현 멘토님 : 현업에서는 코드 커버리지 검사 결과가 최소 80% 이상이 나오게 테스트 코드를 작성하는 것을 목표로 합니다.
- 코드 커버리지란 : 소프트웨어의 테스트 케이스가 실제 코드를 얼마나 반영하는지 나타내는 지표 중 하나
- 라인 커버리지 : 코드 한 줄이 한 번 이상 실행된다면 충족
- 결정 커버리지(브랜치 커버리지) : 모든 조건식의 내부 조건이 true/false를 가진다면 충족
- 조건 커버리지 : if 조건문 안의 개별 조건식이 true/false인 경우 모두 실행되었을 때 충족. 사실상 결정 커버리지보다 더 detail한 커버리지인 것
- 스프링에서 Jacoco 라이브러리를 통해 코드 커버리지 검사를 할 수 있다.
- 참고 사이트 : https://yinq.tistory.com/211
- 코드 커버리지란 : 소프트웨어의 테스트 케이스가 실제 코드를 얼마나 반영하는지 나타내는 지표 중 하나
(2) @DeleteMapping 관련 에러 해결법은?!
- @DeleteMapping + thymeleaf 사용시 Whitelabel Error Page 에러가 발생하였다!!
- 해결방법 : form 태그를 통해 경로 요청하기
- 참고 사이트 : https://kth990303.tistory.com/51
(3) 다음 단계로 넘어갈 때 기존 코드를 가져오는 방법은?
- 원격 레포지토리 경로 재설정
1. 새로운 레포지토리 클론하기
cd ..
git clone https://github.com/yourusername/spring-gift-wishlist.git
cd spring-gift-wishlist
2. spring-gift-product 원격 레포지토리 추가하기
git remote add product ../spring-gift-product
git fetch product
3. 병합하기
- spring-gift-product의 step1 merge into spring-gift-wishlist의 step0
- ⭐️ ⭐️ 이때, histories가 relate되어 있지 않아 에러가 난다면 아래의 명령어를 입력한다.
git merge jpa/step3 --allow-unrelated-histories
4. 병합 후 원격 레포지토리 제거
git remote remove product
(4) 생성된 새로운 리소스의 위치를 클라이언트에게 알리는 방법??
- Location 헤더를 활용한다.
(5) 구글링 팁 좀 주세요!!
- spring docs responseentity와 같이 docs를 통해 공식 문서 내용을 찾아보자
- site:htttps://docs.spring.io responseentity와 같이 사이트 내에서 키워드 검색을 해보자
(6) 트러블 슈팅 잘하는 방법?
- 에러 로그 및 에러 상황 이해하기
- 에러 해결을 위한 코드의 변경사항은 하나씩만 적용하기
- 사용하는 브라우저에서 개발자 도구를 익숙하게 사용할 줄 알 것
(7) 무조건 API 명세를 지켜야만 할까?
- 현실적으로 모든 사항을 따를 순 없다.
- 다른 서비스들을 참고하자!!
(8) HTTP 상태 코드는 어떤 것이 있을까?
- 1XX : Informational (정보 제공 : 클라이언트의 요청을 받았으며 작업을 계속 진행하고 있음)
- 2XX : Success (성공 : 클라이언트가 요청한 동작을 수신하여 이해하였고 승낙하였으며 성공적으로 처리함)
- 4XX : Client Error (클라이언트 에러 : 클라이언트 요청에 오류가 있음)
- 400 : Bad Request (요청의 구문이 잘못되었음)
- 401 : Unauthorized (지정한 리소스에 대한 액세스 권한이 없음)
- 403 : Forbidden (지정한 리소스에 대한 액세스가 금지됨)
- 404 : Not Found (지정한 리소스를 찾을 수 없음)
- 5XX : Server Error (서버 에러 : 클라이언트의 요청은 유효하지만 서버 처리에 실패함)
- 500 : Internal Server Error (서버에 에러가 발생함)
- 501 : Not Implemented (요청한 URI의 메소드에 대해 서버가 구현되고 있지 않음)
- 502 : Bad Gateway (게이트웨이 또한 프록시 역할을 하는 서버가 그 뒷단의 서버로부터 잘못된 응답을 받음)
(9) 멱등성이란?
- 여러 번의 동일한 요청에 대해 서버에 미치는 영향이 동일한 경우
(10) SSG (Static Site Generation)이란?
- build 타임에 HTML 페이지를 미리 생성하여 서버에 배포하는 방식. 즉, 사용자 요청 시, 사전에 렌더링되어 있는 정적 파일이 제공됨
- 장점
- 매우 빠른 페이지 로딩 속도
- 낮은 서버 부하
- 단점
- 동적 콘텐츠 제공이 어려움.
- 빌드 타임이 길어질 수 있음
(11) ISR (Incremental Static Regeneration)이란?
- SSG의 정적 사이트 생성의 이점을 유지하며 실시간 데이터 갱신을 가능하게 한다.
- 동작 방식
- 초기 정적 페이지 생성
- 빌드 타임에 일부 또는 전체 페이지를 정적으로 생성하여 서버에 배포한다. (이 과정은 SSG와 동일함)
- 사용자의 페이지 요청
- 서버는 이미 생성된 정적 페이지를 곧바로 제공한다.
- 백그라운드 재생성
- 지정된 재생성 주기(예 : 60초)가 지난 후 동일한 페이지에 대한 요청이 들어오면, 서버는 해당 페이지를 최신 데이터로 재생성한다.
- 사용자는 캐시에 저장된 이전 버전의 페이지를 받고, 다음 요청 시 재생성된 최신 페이지를 받는다는 단점이 있다.
- 캐시 업데이트
- 백그라운드에서 재생성이 완료되면, 새로운 정적 페이지가 캐시를 대체한다.
- 초기 정적 페이지 생성
- 장점
- 빠른 초기 응답
- 비교적 효율적인 서버 자원 사용
- 단점
- 초기 데이터 변경 지연
- 사용자 요청 시 곧바로 재생성된 페이지를 주는 것이 아니다.
- 복잡성
- 구현이 복잡해질 수 있다.
- 서버 부하 증가
- 초기 데이터 변경 지연
(12) record 사용 vs 그냥 class 만들기
- 기본적인 도메인 class의 객체로서는 class가 낫다!
- immutable한 특성으로 인한 제약
- record는 모든 필드가 기본적으로 final로 선언되어 불변이므로 상태 변경이 필요할 때 새로운 객체를 생성해야 한다. 이는 성능과 메모리 사용 측면에서 비효율적일 수 있다.(예를 들어 Product 객체의 가격이나 재고 수량이 변할 수 있는 상황 등)
- 프록시 객체 사용시 제약
- ORM 프레임워크나 AOP 등을 사용할 때 프록시 객체가 필요할 수 있다. record는 프록시 객체 생성에 적합하지 않을 수 있으며, 이러한 프레임워크와의 호환성이 떨어질 수 있다.
- 상속 제약
- record로 상속을 구현하기 어렵다.
- immutable한 특성으로 인한 제약
- ⭐️ ⭐️ 단, record는 불변성과 equals()와 같은 메서드가 자동으로 구현되어 있다는 점에서 DTO에서는 더 나을 거 같긴 하다.
(13)⭐️⭐️ @Valid와 @Validated에 대하여
- @Valid는 객체에 관하여 미리 정해둔 규칙(예 : Notnull, Size 등)을 확인하는 유효성 검사이고, @Validated는 미리 정해둔 규칙으로 확인할 수 없는 규칙(예 : 객체값의 중복성 검사 등)을 확인하는 유효성 검사
- @Valid
- 메서드 파라미터로 전달된 객체의 유효성을 검증한다. 이때, 객체 내부에 또 다른 객체가 포함되어 있는 경우, 내부 객체의 유효성도 함께 검사한다.
- 예를 들어 아래와 같이 객체 설계 시 유효성 검증할 내용을 미리 정해둔다면, @Valid 애노테이션을 활용하여 메서드 파라미터로 전달되는 객체의 유효성을 검증할 수 있따.
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
public class Product {
@NotNull
private Long id;
@NotNull
@Size(min = 2, max = 30)
private String name;
// getters and setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
import gift.product.model.Product;
import gift.product.repository.ProductRepository;
import jakarta.validation.Valid;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class ProductService {
private final ProductRepository productRepository;
public ProductService(ProductRepository productRepository) {
this.productRepository = productRepository;
}
public List<Product> getAllProducts() {
return productRepository.findAll();
}
public Product getProductById(Long id) {
return productRepository.findById(id).orElse(null);
}
@Transactional
public Product createProduct(@Valid Product product) {
checkForDuplicateProduct(product);
return productRepository.save(product);
}
@Transactional
public Product updateProduct(@Valid Product product) {
checkForDuplicateProduct(product);
return productRepository.save(product);
}
@Transactional
public void deleteProduct(Long id) {
productRepository.deleteById(id);
}
private void checkForDuplicateProduct(Product product) {
List<Product> products = productRepository.findAll();
for (Product p : products) {
if (p.getName().equals(product.getName())) {
throw new ProductAlreadyExistsException(product.getName());
}
}
}
}
- @Validated
- 특정 검증 그룹(메서드나 클래스 레벨)을 지정하여 유효성 검사를 할 수 있다.
- 예를 들어 아래와 같이 생성할 때와 수정할 때, 서로 다른 유효성 검사를 적용할 수 있다.
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import org.springframework.validation.annotation.Validated;
public class Product {
public interface Create {}
public interface Update {}
@NotNull(groups = Update.class)
private Long id;
@NotNull(groups = {Create.class, Update.class})
@Size(min = 2, max = 30, groups = {Create.class, Update.class})
private String name;
// getters and setters
}
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
@Service
@Validated
public class ProductService {
public Product createProduct(@Validated(Product.Create.class) Product product) {
// 생성 시 검증 로직
return product;
}
public Product updateProduct(@Validated(Product.Update.class) Product product) {
// 업데이트 시 검증 로직
return product;
}
}
(14)⭐️⭐️ @Transactional이란?
- 메서드나 클래스에 트랜잭션을 적용하기 위해 사용된다.
- 트랜잭션(transaction) : 데이터베이스 작업을 논리적으로 하나의 단위로 묶어주는 기능. 작업이 모두 성공하거나 모두 실패하도록 보장한다. 이를 통해 데이터의 일관성과 무결성을 유지할 수 있다.
- 주로 데이터 일관성을 보장하기위해 DB와의 상호작용이 있는 서비스 계층에서 사용된다. 또한 @Transactional(readOnly = true)와 같이 여러 추가적인 기능들도 존재한다!
- 아래와 같이 save 메서드와 updateInventory 메서드가 모두 성공해야 DB에 작업된 값이 등록된다. 이때, 하나라도 실패하면 트랜잭션이 롤백된다.
@Service
public class OrderService {
private final OrderRepository orderRepository;
private final InventoryService inventoryService;
public OrderService(OrderRepository orderRepository, InventoryService inventoryService) {
this.orderRepository = orderRepository;
this.inventoryService = inventoryService;
}
@Transactional
public void placeOrder(Order order) {
orderRepository.save(order);
inventoryService.updateInventory(order);
// 모든 작업이 성공적으로 완료되면 트랜잭션 커밋
// 하나라도 실패하면 트랜잭션 롤백
}
}
[15~19] 인증과 인가에 대하여
(15) 인증과 인가에 대하여
- 질문 : 보통 현업에서는 어떤 방식을 통해 인증/인가를 할지 궁금합니다!
- 양재현 멘토님
- Spring Security + JWT (JSON Web Token):
설명: JWT는 서버에서 생성되고 클라이언트가 저장하여 요청마다 서버에 전송하는 자격 증명입니다.
장점: 무상태(stateless) 인증, 확장성 뛰어남, 세션 유지 필요 없음. - Spring Security + OAuth2/OpenID Connect:
설명: OAuth2는 제3자 애플리케이션이 사용자의 자원에 접근할 수 있도록 하는 권한 부여 프레임워크이며, OpenID Connect는 OAuth2 위에 구축된 인증 프로토콜입니다.
장점: 소셜 로그인 통합 용이, 중앙 집중식 인증 구현 가능. - Spring Security + Basic Auth / Digest Auth:
설명: HTTP Basic Auth는 사용자 이름과 비밀번호를 HTTP 헤더에 포함하여 인증하고, Digest Auth는 이를 해싱하여 전송하는 방식입니다.
장점: 구현 간단, 테스트 및 프로토타이핑에 유용, HTTPS 필수. - Spring Security + Session-Based Authentication:
설명: 전통적인 세션 기반 인증으로, 사용자가 로그인하면 서버가 세션을 생성하고 클라이언트는 세션 ID를 쿠키에 저장하여 사용합니다.
장점: 이해하기 쉽고 브라우저 기반 애플리케이션에 적합, CSRF 방어 가능, 서버에서 사용자 상태 관리.- 가장 많이 사용하는 방식은 아무래도 JWT 이지만 발급 받은 토큰키를 탈취당하면 곤란합니다.
따라서 가장 최소한의 보안장치를 권장합니다.
/register 등록할때 email과 password를 암호화해서 넘겨줘야합니다. 해당 응용방법은 아래 사이트 참조하셔요 ( AES )
- https://oingdaddy.tistory.com/233
- 가장 많이 사용하는 방식은 아무래도 JWT 이지만 발급 받은 토큰키를 탈취당하면 곤란합니다.
- Spring Security + JWT (JSON Web Token):
(16) JWT 토큰이란?
- JWT 토큰은 보안성 있는 정보 교환을 위해 사용되며, 크게 세 부분으로 나뉜다.
- 헤더(Header) : 토큰의 메타데이터를 포함하며 JSON 객체로 구성된다.
- 타입 (typ) : 토큰의 타입을 나타내며, JWT 토큰임을 나타내기 위해 "JWT"로 설정된다.
- 알고리즘 (alg) : 토큰의 서명을 검증하는 데 사용된 해싱 알고리즘을 나타낸다.
- 페이로드(Payload) : 토큰의 실제 데이터인 클레임을 포함한다.
- 클레임 : JWT에 포함된 데이터 조각이며, 주로 사용자와 관련된 정보, 만료 시간 등의 데이터들을 포함한다.
- 서명(Signature) : 헤더와 페이로드에 대해 지정된 알고리즘을 사용해 서명을 생성한다.
- 헤더와 페이로드를 Base64 Url로 인코딩한 후 서명을 연결하여 JWT를 생성한다.
- 헤더(Header) : 토큰의 메타데이터를 포함하며 JSON 객체로 구성된다.
// 서명은 아래와 같다.
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
// JWT 토큰은 아래와 같다.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
- 주로 access token과 refresh token을 만드는데 사용한다. 인증 과정에서 JWT 토큰이 해당 애플리케이션의 secret key로 만들어졌는지 확인한다. access token이 탈취되는 경우를 방지하여 refresh token을 만든다.
- 양재현 멘토님 : 해당 시크릿키는 외부에 노출되면 또 위험하기 때문에 관리를 잘 해주셔야 합니다.
보통 현업에서는 이 시크릿키를 한번더 암호화해서 사용하여 관련 라이브러리 소개드립니다.- Jasypt
- 참고 주소 : https://bepoz-study-diary.tistory.com/200
예를 들어 아래와 같이 적용할수 있습니다.
- 참고 주소 : https://bepoz-study-diary.tistory.com/200
- Jasypt
jwt.secret.key=ENC(SHZ3Wn9WLIBcQXX3hG5/7WHoIvkR+UF0fYYerwztivYoE8ec6vM4wAKLxhjRjSMS)
(17) UUID란?
- 모든 장치에서 고유하게 식별될 수 있는 128비트 숫자. 전 세계적으로 고유한 식별자를 생성하는데 사용된다. 주로 객체의 id값을 고유하게 식별해야할 때 사용한다.
(18) beearer 인증방식이란?
- OAuth 2.0 표준에 따라 정의된 인증 매커니즘 중 하나이다. 로그인 후 클라이언트가 서버에 요청할 때마다 Authorization 헤더에 Bearer 토큰을 포함하여 전송한다. (Bearer 토큰은 보안적으로 민감한 정보이므로 HTTPS를 통해 전송되어야 한다.) 이때, Bearer 토큰은 주로 JWT 토큰이다.
(19) @ProblemDetail이란?
- Spring MVC에서 발생한 예외를 표준화된 JSON 형식의 오류 응답 형식으로 반환할 때 사용한다.
- type, title, status, detail, instance 등의 필드를 포함한다.
- title : 오류 유형에 대한 간단한 설명. 클라이언트가 오류의 종류를 이해하는데 도움이 된다!
- detail : 오류에 대한 구체적인 설명을 설정.
- type : 오류 유형을 나타내는 URI를 설정
- instance : 오류가 발생한 구체적인 인스턴스(리소스)를 나타내는 URI를 설정
(20) layered architecture의 핵심은?
- 의존성이 한 방향으로 흘러야 한다는 것
(21) HandlerMethodArgumentResolver에 대하여
- 질문 : HandlerMethodArgumentResolver와 같이 자주 사용되는 HandlerAdapter는 어떤 것이 있을지 궁금합니다!
- 양재현 멘토님
- HandlerMethodArgumentReslover에 RequestMappingHandlerAdapter, SimpleControllerHandlerAdapter, HttpRequestHandlerAdapter 정도 사용하며 보통 HandlerMethodArgumentResolver로 해결합니다.
2. 계절학기 대면수업 내용
(1) 객체는 정보보다는 행위에 집중해야 한다.
- 객체 설계 시 메서드부터 짜고 속성값 설정하기!!
(2) 인터페이스는 계약이다.
- repository 인터페이스를 짜는 것은 지극히 당연한 계약이다.
(3) 반환되는 requestbody에 내용을 풍부하게 담자!
(4) HTTP의 핵심은 stateless하다는 것
- 요청과 응답은 독립적이고 서버는 클라이언트의 상태 정보를 유지하지 않는다.
- frontend에서 보내주는 값들은 무조건 JSON이나 form의 형태이다.
- 전부 검증하자!
- 인증은 주니어 시절 배워야할 가장 각별한 것이다.
- 상태 관리를 DB에서 하자
- DB에서도 check를 통해 검증하자
3. 이번주차 과제 수행과정
https://github.com/kakao-tech-campus-2nd-step2/spring-gift-wishlist/pull/414
4. 추가적인 내용들
(1) 크롬 개발자 도구 생활화하기
- 크롬 브라우저에서 F12 누르면 표시되는 그것!
- 기본 기능들
- Elements : HTML 요소 탭 (현재 웹페이지의 소스를 보여줌)
- Console : 웹브라우저에서 발생하는 모든 메시지 표시
- Sources : 소스 보기 (CSS, JS, 이미지, 동영상 등 리소스들 볼 수 있음)
- Network : 네트워크 (웹페이지와 웹페이지에 포함된 모든 리소스들이 간의 네트워크 전송 정보 확인 가능)
- Performance : 페이지 로딩 성능 측정
- Memeory : 메모리 사용량 모니터링
- Application : 어플리케이션 실행 정보 ex) 프로세스, 스토리지, 캐시 사용 정보
- Securiry : 보안 (현재 웹 페이지의 도메인 보안 접속 결과를 보여줌)
- Lighthouse : 리포트 도구 (현재 웹 페이지에 대한 분석 결과를 리포트로 제공함)
(2) 토큰 생성 및 검증할 때 반드시 같은 key값으로 비교하자!
(3) 라이브러리 버전에 따른 변화를 꼭 주의하자!
(4) CRUD할 때
- 각 로직을 controller -> service -> repository 순으로 설계
(5) 엔드 포인트란?
(6) layered architecture의 핵심은?
- 의존성이 한 방향으로 흘러야 한다는 것
(7) 코드는 최대한 깔끔하게 유지하자 (1.보다는 2.로)
// 1.
@DeleteMapping("/{id}")
public String deleteProduct(@PathVariable("id") Long id) {
Product product = productService.getProductById(id);
if (product == null) {
throw new ProductNotFoundException(id);
}
productService.deleteProduct(id);
return "redirect:/products";
}
// 2.
@DeleteMapping("/{id}")
public String deleteProduct(@PathVariable("id") Long id) {
if (!productService.deleteProduct(id)) {
throw new ProductNotFoundException(id);
}
return "redirect:/products";
}
'Backend > Spring' 카테고리의 다른 글
[세차새차] 양방향 매핑 지양과 순환 참조의 위험성 - 회원 관리 기능 개발 (2) | 2024.08.31 |
---|---|
[Kakao Tech Campus Step2] 1주차 회고 (1) | 2024.06.30 |
[Spring Core] 초록 스터디 Step3 회고 (0) | 2024.05.03 |
[Spring MVC] 초록 스터디 Step2 회고 (1) | 2024.04.22 |
[Spring MVC] 초록 스터디 Step1 회고 (1) | 2024.04.14 |