유튜브 강의(https://www.youtube.com/watch?v=htYYSszfzv0&t=5431s)
기본 기능 완료
예제의 경우, 필드 주입 AutoWired를 이용하는 듯 하다.
-> 생성자 주입으로 적용해보기.
생성자 주입을 사용하는 이유는 다음과 같습니다.
1. 순환 참조를 방지할 수 있다.
순환 참조가 발생하면 애플리케이션이 구동되지 않습니다. 생성자 주입을 사용하면 객체가 생성될 때 모든 의존성을 주입하므로 순환 참조가 발생할 가능성이 줄어듭니다.
2. 테스트 코드 작성이 편리하다.
생성자 주입을 사용하면 단순한 POJO를 이용한 테스트 코드를 만들 수 있습니다. 의존성을 명시적으로 주입하기 때문에 의존성 없이도 객체를 생성할 수 있습니다.
3. 나쁜 냄새를 없앤다.
필드 주입이나 수정자 주입을 사용하면 객체 생성 후에 의존성을 주입합니다. 이는 객체 생성 후에 객체의 상태를 변경하는 것이므로 나쁜 냄새를 유발할 수 있습니다. 생성자 주입을 사용하면 객체가 생성될 때 모든 의존성이 주입되므로 객체 생성
4. immutable 하다.
생성자 주입을 사용하면 객체가 생성될 때 모든 의존성이 주입되므로 생성 후에는 의존성이 변경되지 않습니다. 이는 실행 중에 객체가 변하는 것을 막아 오류를 사전에 방지할 수 있습니다.
5. 코드 품질을 높일 수 있다.
생성자 주입은 코드의 가독성을 높이고 유지보수성을 높일 수 있습니다. 생성자 주입을 사용하면 객체 생성 시점에서 모든 의존성이 주입되므로 객체의 생성 과정이 명확해지며, 코드의 의도를 명확하게 전달할 수 있습니다.
아래 링크에 예제까지 해서 DI에 대해 잘 정리되었다.
자세한 예시 : https://madplay.github.io/post/why-constructor-injection-is-better-than-field-injection
기본적인 흐름만 참고할 수 있도록 강의가 구성되어 있어
간단한 로그인 -> 장바구니 담기 -> 주문(주문서 작성) -> 주문 내역 확인이 가능하다.
소스
OrderController
- 주문에 대한 컨트롤러
import com.example.study_springbootvue.dto.OrderDto;
import com.example.study_springbootvue.entity.Order;
import com.example.study_springbootvue.repository.OrderRepository;
import com.example.study_springbootvue.service.JwtService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;
import java.util.List;
@RestController
public class OrderController {
@Autowired
JwtService jwtService;
private final OrderRepository orderRepository;
public OrderController(OrderRepository orderRepository) {
this.orderRepository = orderRepository;
}
@GetMapping ("/api/orders")
public ResponseEntity getOrders(
@CookieValue(value = "token", required = false) String token
){
if(!jwtService.isValid(token)){
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
}
List<Order> orderList = orderRepository.findAll();
return new ResponseEntity(orderList, HttpStatus.OK);
}
@PostMapping("/api/orders")
public ResponseEntity pushOrder(@RequestBody OrderDto dto,
@CookieValue(value = "token", required = false) String token)
{
if(!jwtService.isValid(token)){
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
}
Order newOrder = new Order();
newOrder.setMemberId(jwtService.getId(token));
newOrder.setName(dto.getName());
newOrder.setAddress(dto.getAddress());
newOrder.setPayment(dto.getPayment());
newOrder.setCardNumber(dto.getCardNumber());
newOrder.setItems(dto.getItems());
orderRepository.save(newOrder);
return new ResponseEntity<>(HttpStatus.OK);
}
}
CartController (장바구니 컨트롤러)
import com.example.study_springbootvue.entity.Cart;
import com.example.study_springbootvue.entity.Item;
import com.example.study_springbootvue.repository.CartRepository;
import com.example.study_springbootvue.repository.ItemRepository;
import com.example.study_springbootvue.service.JwtService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;
import java.util.List;
@RestController
public class CartController {
@Autowired
JwtService jwtService;
private final CartRepository cartRepository;
private final ItemRepository itemRepository;
public CartController(CartRepository cartRepository, ItemRepository itemRepository) {
this.cartRepository = cartRepository;
this.itemRepository = itemRepository;
}
@GetMapping("/api/cart/items")
public ResponseEntity getcartItmes(@CookieValue(value = "token", required = false) String token){
if(!jwtService.isValid(token)){
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
}
int memberId = jwtService.getId(token);
List<Cart> carts = cartRepository.findByMemberId(memberId);
List<Integer> itemIds = carts.stream().map(Cart::getItemId).toList();
List<Item> items = itemRepository.findByIdIn(itemIds);
return new ResponseEntity(items, HttpStatus.OK);
}
@PostMapping ("/api/cart/items/{itemId}")
public ResponseEntity pushCartItme(@PathVariable("itemId") int itemId,
@CookieValue(value = "token", required = false) String token)
{
if(!jwtService.isValid(token)){
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
}
int memberId = jwtService.getId(token);
Cart cart = cartRepository.findByMemberIdAndItemId(memberId, itemId);
if(cart == null){
Cart newCart = new Cart();
newCart.setMemberId(memberId);
newCart.setItemId(itemId);
cartRepository.save(newCart);
}
return new ResponseEntity<>(HttpStatus.OK);
}
@DeleteMapping("/api/cart/items/{itemId}")
public ResponseEntity removeCartItem(
@PathVariable("itemId") int itemId,
@CookieValue(value = "token", required = false) String token
){
if(!jwtService.isValid(token)){
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED);
}
int memberId = jwtService.getId(token);
Cart cart = cartRepository.findByMemberIdAndItemId(memberId, itemId);
cartRepository.delete(cart);
return new ResponseEntity(HttpStatus.OK);
}
}
Order Entity(주문)
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
@Entity
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column
private int memberId;
@Column(length = 50, nullable = false)
private String name;
@Column(length = 50, nullable = false)
private String address;
@Column(length = 20, nullable = false)
private String payment;
@Column(length = 40, nullable = false)
private String cardNumber;
@Column(length = 500, nullable = false)
private String items;
}
Cart Entity(장바구니)
@Getter
@Setter
@Entity
@Table(name = "carts")
public class Cart {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column
private int memberId;
@Column
private int itemId;
}
수정
JwtServiceImpl
Valild 체크를 위한 isVaild() 함수 추가
멤버 ID 확인용 getId() 추가
import io.jsonwebtoken.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@Service
public class JwtServiceimpl implements JwtService {
private final String secretKey = "dfadfksfjadfaflsdfnklbnlknklfdfdlff123213123";
@Override
public String getToken(String key, Object value) {
Date expireTime = new Date();
expireTime.setTime(expireTime.getTime() * 1000 * 60 * 5);
byte[] secretByteKey = DatatypeConverter.parseBase64Binary(secretKey);
Key signKey = new SecretKeySpec(secretByteKey, SignatureAlgorithm.HS256.getJcaName());
Map<String, Object> headerMap = new HashMap<>();
headerMap.put("typ", "JWT");
headerMap.put("alg", "HS256");
Map<String, Object> map = new HashMap<>();
map.put(key, value);
JwtBuilder builder = Jwts.builder().setHeader(headerMap)
.setClaims(map)
.setExpiration(expireTime)
.signWith(signKey, SignatureAlgorithm.HS256);
return builder.compact();
}
@Override
public Claims getClaims(String token) {
if (token != null && !"".equals(token)) {
try {
byte[] secretByteKey = DatatypeConverter.parseBase64Binary(secretKey);
Key signKey = new SecretKeySpec(secretByteKey, SignatureAlgorithm.HS256.getJcaName());
return Jwts.parserBuilder().setSigningKey(signKey).build().parseClaimsJws(token).getBody();
} catch (ExpiredJwtException e) {
// 만료됨
log.error("토큰 만료");
} catch (JwtException e) {
// 유효하지 않음
log.error("토큰 유효하지 않음");
}
}
return null;
}
@Override
public boolean isValid(String token) {
return this.getClaims(token) != null;
}
@Override
public int getId(String token) {
Claims claims = this.getClaims(token);
if(claims != null){
return Integer.parseInt(claims.get("id").toString());
}
return 0;
}
}
'프로그래밍' 카테고리의 다른 글
[DB] Oracle DB에서 MariaDB 로 데이터 이관하기 (0) | 2023.04.17 |
---|---|
[크론탭] Crontab 명령어 정리 (0) | 2023.04.14 |
[Spring MVC] 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술(1) (0) | 2023.04.09 |
[SpringBoot+Vue.js] 정리 (4) JWT Token (0) | 2023.04.07 |
[SpringBoot+Vue.js] 정리 (3) Vue router 추가 및 로그인 (0) | 2023.04.06 |