본문 바로가기
🛠 BackEnd/Spring

[ Spring ] Controller, Service Repository 코드 분리 (1/2)

by 깸뽀 2022. 10. 8.
728x90

💻  AllInOneController.java 

import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

@RequiredArgsConstructor // final로 선언된 멤버 변수를 자동으로 생성합니다.
@RestController // JSON으로 데이터를 주고받음을 선언합니다.
public class AllInOneController {

    // 신규 상품 등록
    @PostMapping("/api/products")
    public Product createProduct(@RequestBody ProductRequestDto requestDto) throws SQLException {
        // 요청받은 DTO 로 DB에 저장할 객체 만들기
        Product product = new Product(requestDto);

        // DB 연결
        Connection connection = DriverManager.getConnection("jdbc:h2:mem:springcoredb", "sa", "");

        // DB Query 작성
        PreparedStatement ps = connection.prepareStatement("select max(id) as id from product");
        ResultSet rs = ps.executeQuery();
        if (rs.next()) {
            // product id 설정 = product 테이블의 마지막 id + 1
            product.setId(rs.getLong("id") + 1);
        } else {
            throw new SQLException("product 테이블의 마지막 id 값을 찾아오지 못했습니다.");
        }
        ps = connection.prepareStatement("insert into product(id, title, image, link, lprice, myprice) values(?, ?, ?, ?, ?, ?)");
        ps.setLong(1, product.getId());
        ps.setString(2, product.getTitle());
        ps.setString(3, product.getImage());
        ps.setString(4, product.getLink());
        ps.setInt(5, product.getLprice());
        ps.setInt(6, product.getMyprice());

        // DB Query 실행
        ps.executeUpdate();

        // DB 연결 해제
        ps.close();
        connection.close();

        // 응답 보내기
        return product;
    }

    // 설정 가격 변경
    @PutMapping("/api/products/{id}")
    public Long updateProduct(@PathVariable Long id, @RequestBody ProductMypriceRequestDto requestDto) throws SQLException {
        Product product = new Product();

        // DB 연결
        Connection connection = DriverManager.getConnection("jdbc:h2:mem:springcoredb", "sa", "");

        // DB Query 작성
        PreparedStatement ps = connection.prepareStatement("select * from product where id = ?");
        ps.setLong(1, id);

        // DB Query 실행
        ResultSet rs = ps.executeQuery();
        if (rs.next()) {
            product.setId(rs.getLong("id"));
            product.setImage(rs.getString("image"));
            product.setLink(rs.getString("link"));
            product.setLprice(rs.getInt("lprice"));
            product.setMyprice(rs.getInt("myprice"));
            product.setTitle(rs.getString("title"));
        } else {
            throw new NullPointerException("해당 아이디가 존재하지 않습니다.");
        }

        // DB Query 작성
        ps = connection.prepareStatement("update product set myprice = ? where id = ?");
        ps.setInt(1, requestDto.getMyprice());
        ps.setLong(2, product.getId());

        // DB Query 실행
        ps.executeUpdate();

        // DB 연결 해제
        rs.close();
        ps.close();
        connection.close();

        // 응답 보내기 (업데이트된 상품 id)
        return product.getId();
    }

    // 등록된 전체 상품 목록 조회
    @GetMapping("/api/products")
    public List<Product> getProducts() throws SQLException {
        List<Product> products = new ArrayList<>();

        // DB 연결
        Connection connection = DriverManager.getConnection("jdbc:h2:mem:springcoredb", "sa", "");

        // DB Query 작성 및 실행
        Statement stmt = connection.createStatement();
        ResultSet rs = stmt.executeQuery("select * from product");

        // DB Query 결과를 상품 객체 리스트로 변환
        while (rs.next()) {
            Product product = new Product();
            product.setId(rs.getLong("id"));
            product.setImage(rs.getString("image"));
            product.setLink(rs.getString("link"));
            product.setLprice(rs.getInt("lprice"));
            product.setMyprice(rs.getInt("myprice"));
            product.setTitle(rs.getString("title"));
            products.add(product);
        }

        // DB 연결 해제
        rs.close();
        connection.close();

        // 응답 보내기
        return products;
    }
}
 
 
 

 

💻  ProductRequestDto.java : 관심상품 등록 요청 DTO

import lombok.AllArgsConstructor;
import lombok.Getter;

@NoArgsConstructor
@AllArgsConstructor
@Getter
public class ProductRequestDto {
        // 관심상품명
    private String title;
        // 관심상품 썸네일 image URL
    private String image;
    // 관심상품 구매링크 URL
    private String link;
        // 관심상품의 최저가
    private int lprice;
}

 

 

💻  Product.java : 관심상품DB 테이블에 대응되는 Entity

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import javax.persistence.*;

@Setter
@Getter // get 함수를 일괄적으로 만들어줍니다.
@NoArgsConstructor // 기본 생성자를 만들어줍니다.
@Entity // DB 테이블 역할을 합니다.
public class Product {

    // ID가 자동으로 생성 및 증가합니다.
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Id
    private Long id;

    // 반드시 값을 가지도록 합니다.
    @Column(nullable = false)
    private String title;

    @Column(nullable = false)
    private String image;

    @Column(nullable = false)
    private String link;

    @Column(nullable = false)
    private int lprice;

    @Column(nullable = false)
    private int myprice;

    // 관심 상품 생성 시 이용합니다.
    public Product(ProductRequestDto requestDto) {
        this.title = requestDto.getTitle();
        this.image = requestDto.getImage();
        this.link = requestDto.getLink();
        this.lprice = requestDto.getLprice();
        this.myprice = 0;
    }
}
 
 
 

💻  ProductMypriceRequestDto.java : 관심상품 최저가 업데이트 요청 DTO

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Getter
public class ProductMypriceRequestDto {
    private int myprice;
}

 

 

 

 
 

출처) 스파르타코딩클럽 수업자료

🔒 AllInOneController 문제점

1. 한개의 클래스에 너무 많은 양의 코드가 존재함

  • 코드 이해가 어렵다 : 처음부터 끝까지 다 읽어야 코드 내용을 이해할 수 있음

2. 현업에서는 코드 추가 혹은 변경 요청이 계속 생김

   [ 변경 요청의 예 ]

        a. 신규 상품 등록 시 Client에게 응답(Response)하는 값 변경

             - 등록된 Product 전체 정보 ⇨ 등록된  Product의 id

        b. 최저가(MyPrice) 업데이트 조건 변경

             - Client가 최저가를 0원 이하로 입력 ⇨ error

        c. DB테이블 이름 변경

             - Product 테이블의 lprice  ⇨ lowprice 변경 

 

🔑  추천 프로그래밍 방식

절차적 프로그래밍 객체지향 프로그래밍 으로 리팩토링

💡 리팩토링이란? 
     : 기능 상의 변경 없이 프로그래밍 구조를 개선하는 것

    1)  하나의 파일에 너무 많은 코드가 들어가지 않게
    2)  역할별로 코드 분리
    3)  코드를 좀 더 읽기 편하게 (가독성)

📌 절차적 프로그래밍(Procedural P-) vs 객체지향 프로그래밍(Object-Oriented P-)

  • 절차적 프로그래밍"
    • 초기 프로그래밍 방식
    • 컴퓨터가 해야할 일들을 쭈~욱 순차적으로 나열해 놓는 코딩 방식
    • 예) AllInOneController 클래스의 각 API 처리내용
  • "객체지향 프로그래밍"
    • 소프트웨어의 규모가 점점 커지면서 필요성이 부각이 됨
    • 대부분의 사람들은 한 번에 여러가지 다른 생각을 하는데 취약
    • 하나의 사물 (객체) 에 하나의 의미를 부여하는 것처럼 프로그래밍하게 됨
      • 예)
        • 뭔가 자를 것이 필요하면 '✂️' 를 떠올림 (class Sciccors)
        • 종이에 적을 게 필요하면 '✏️' 을 떠올림 (class Pen)
        • "하나의 역할" → 객체
728x90

댓글