[SpringBoot+Vue.js] 정리 (5) 기본 기능 완료

유튜브 강의(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;
    }
}