[스파르타코딩클럽] Spring 심화반 1주차

MOON·2021년 9월 12일
0
post-thumbnail

Spring 심화반 1주차

💡기대하는 것

스프링의 핵심개념인 DI와 스프링 IoC컨테이너의 개념을 이해하기
스프링의 Controller, Service, Repository 의 필요성을 이해하기

📝배운것

  • 스프링 프레임워크의 필요성

스프링 학습 전략

  1. 간단한 프로젝트들을 만들어 사용법 위주의 경험을 쌓는다. (예. 웹 개발의 봄 '스프링' 과정)
  2. 그 프로젝트들에 원하는 기능들을 기획하고 추가해 본다.
  3. 필요한 부분들을 중점적으로 학습한다.
    • 자주 막히는 부분 위주로!
    • 어떻게 사용하는게 맞는지 아리송할때!
  4. 스프링 학습은 주제 별로, 이해가 가는 부분까지만 학습 반복
    • 너무 완벽히 이해하려고 하지 말기

✏️AllInOne 코드 작성

Servlet (서블릿)은 자바를 사용하여 웹페이지를 동적으로 생성하는 서버측 프로그램 혹은 그 사양을 말함

package com.sparta.springcore;

import com.fasterxml.jackson.databind.ObjectMapper;

import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.*;
import java.time.LocalDateTime;
import java.util.ArrayList;

@WebServlet(urlPatterns = "/api/products/*", loadOnStartup = 1)
public class AllInOneServlet extends HttpServlet {
    // 신규 관심상품 등록
    // POST /api/products
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 요청 Body 의 JSON -> 자바 객체
        ProductRequestDto requestDto = null;
        StringBuffer jb = new StringBuffer();
        String line = null;
        try {
            BufferedReader reader = request.getReader();
            while ((line = reader.readLine()) != null)
                jb.append(line);

            ObjectMapper objectMapper = new ObjectMapper();
            requestDto = objectMapper.readValue(jb.toString(), ProductRequestDto.class);
        } catch (Exception e) { /*report an error*/ }

        // 요청받은 DTO 로 DB에 저장할 객체 만들기
        Product product = new Product(requestDto);
        LocalDateTime now = LocalDateTime.now();
        product.setCreatedAt(now);
        product.setModifiedAt(now);

        // DB 연결
        try {
            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, created_at, modified_at) 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());
            ps.setString(7, product.getCreatedAt().toString());
            ps.setString(8, product.getModifiedAt().toString());
            // DB Query 실행
            ps.executeUpdate();
            // DB 연결 해제
            ps.close();
            connection.close();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }

        // 자바 객체 -> JSON 으로 변환
        ObjectMapper objectMapper = new ObjectMapper();
        String productJson = objectMapper.writeValueAsString(product);

        // 응답 보내기
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        PrintWriter out = response.getWriter();
        out.print(productJson);
        out.flush();
    }

    // 희망 최저가 변경
		// PUT /api/products/{id}
    @Override
    protected void doPut(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 요청 URL 에 입력되어 있는 'id' 파라미터 조회
        String uri = request.getRequestURI();
        String idStr = uri.substring(uri.lastIndexOf("/") + 1);

        Long id = Long.parseLong(idStr);

        // 요청 BODY 의 JSON -> 자바 객체
        ProductMypriceRequestDto requestDto = null;

        StringBuffer jb = new StringBuffer();
        String line = null;
        try {
            BufferedReader reader = request.getReader();
            while ((line = reader.readLine()) != null)
                jb.append(line);

            ObjectMapper objectMapper = new ObjectMapper();
            requestDto = objectMapper.readValue(jb.toString(), ProductMypriceRequestDto.class);
        } catch (Exception e) { /*report an error*/ }

        Product product = new Product();

        try {
            // 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.setCreatedAt(rs.getTimestamp("created_at").toLocalDateTime());
                product.setModifiedAt(rs.getTimestamp("modified_at").toLocalDateTime());
                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 = ?, modified_at = ? where id = ?");
            ps.setInt(1, requestDto.getMyprice());
            ps.setString(2, LocalDateTime.now().toString());
            ps.setLong(3, product.getId());
            // DB Query 실행
            ps.executeUpdate();
            // DB 연결 해제
            rs.close();
            ps.close();
            connection.close();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }

        // 자바 객체 -> JSON 으로 변환
        ObjectMapper objectMapper = new ObjectMapper();
        String productIdJson = objectMapper.writeValueAsString(product.getId());

        // 응답 보내기 (업데이트된 상품 id)
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        PrintWriter out = response.getWriter();
        out.print(productIdJson);
        out.flush();
    }

		// 등록된 전체 상품 목록 조회
		// GET /api/products
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        ArrayList<Product> products = new ArrayList<>();

        try {
            // 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.setCreatedAt(rs.getTimestamp("created_at").toLocalDateTime());
                product.setModifiedAt(rs.getTimestamp("modified_at").toLocalDateTime());
                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();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }

        // 자바 객체 -> JSON 으로 변환
        ObjectMapper objectMapper = new ObjectMapper();
        String productsJson = objectMapper.writeValueAsString(products);

        // 응답 보내기
        response.setHeader("Content-Type", "application/json");
        response.setCharacterEncoding("UTF-8");
        PrintWriter out = response.getWriter();
        out.print(productsJson);
        out.flush();
    }
}

문제점

코드가 복잡해지고 중복코드가 많아진다.
가독성이 떨어진다. 하나를 바꾸려면 오래걸린다.

✏️코드의 역할과 분리

Controller의 역할

@Controller 는 스프링 서버 개발자 입장에서는 시작점과 끝점으로 보이지만, 사실 스프링이 사용자의 요청 (Request) 과 응답 (Response) 을 처리해 주고 있습니다.

Service의 역할

  • 비즈니스 로직을 처리
    • 비즈니스 로직: 서버에서 사용자의 요구사항 처리
  • DB 정보가 필요할 때는 Repository에게 전담

Repository의 역할

  • DB 관리 (연결, 해제, 자원 관리)
  • DB CRUD 작업 처리

역할을 분리

객체지향적으로 프로그래밍을 해야 한다.

  • 초기 프로그래밍 방식은 "절차적 프로그래밍" 이었습니다. 앞 챕터의 AllInOneController 클래스의 각 API 처리내용처럼 해야할 일들을 쭈~욱 나열해 놓는 코딩 방식이죠.
  • 하지만 대부분의 사람들은 한 번에 여러가지 다른 생각을 하는데 취약합니다. 그래서 우리가 하나의 사물 (객체) 에 하나의 의미를 부여하는 것처럼 프로그래밍하게 되었습니다.
  • 예를 들어, 뭔가 자를 것이 필요하면 '✂️' 를 떠올리고, 종이에 적을 필요가 있으면 '✏️' 을 떠올리는 것처럼, "한 번에 하나의 역할"을 하는 객체를 만들어 프로그래밍 하게 된 거죠!
  • 또 다른 예 (메모)
    • 생각나는데로 적는게 편하다 (절차적 프로그래밍)
    • 메모 양이 많아지면, 주제별로 정리해 둬야 검색이 용이하다 (객체지향 프로그래밍)

✏️스프링 IoC 컨테이너 사용하기

스프링은 DI를 통해 객체를 대신 생성해준다. 스프링이 생성해주는 객체를 빈(Bean)이라고 한다. 이 빈을 모아둔 곳이 IoC 컨테이너. 우리는 IoC컨테이너에 등록되어있는 빈을 사용하면 된다. 기본적으로 싱글턴 형태로 되어있다.

  • 빈 (Bean): 스프링이 생성해주는 객체
  • 스프링 IoC 컨테이너: 빈을 모아둔 통

✏️스프링 프레임워크 재이해

스프링 프레임워크란?

The Spring Framework provides a comprehensive programming and configuration model for modern Java-based enterprise applications - on any kind of deployment platform.

* 출처: 스프링 공식 홈페이지

  • Enterprise applications 개발 편의성 제공
    • 기업용 애플리케이션?
      • 고객 대상 웹 서비스
        • 네이버, 쿠팡, 인스타그램 등
      • 적용 범위가 빠르게 확대되는 중
        • IT 기업 → 모든 기업 (병원, 보험회사 등)
        • 외부고객 → 내부 고객 (사내 결재 시스템, 사내 출/퇴근 기록 시스템)
        • 기업 → 개인 사업자
    • 스프링은 결국 기업용 애플리케이션의 요구사항 해결에 초점을 맞춘 프레임워크
    • 기업용 애플리케이션 특성
      • 신뢰성이 중요
        • 병원에서 수술 시 환자 기록이 바뀐다면?
      • 서버의 안정성 유지 중요
        • 복권 실시간 추첨에 서버 다운?
      • 데이터 관리가 중요
        • 막대한 양의 데이터 관리 필요
        • 여러 사용자가 동시 접속 시 데이터 일관성
        • → 대부분 DB (데이터베이스) 사용

A key element of Spring is infrastructural support at the application level: Spring focuses on the "plumbing" of enterprise applications so that teams can focus on application-level business logic, without unnecessary ties to specific deployment environments.

* 출처: 스프링 공식 홈페이지

비즈니스 로직에 집중하게 해 준다?

  • 웹 서비스 다이어그램

  • 서버 개발자들이 신경써야 할 부분이 너무 많음
    1. API: 클라이언트 ↔ 서버
    2. 비즈니스 로직: 서버에서 사용자의 요구사항 처리
    3. DB: 서버 ↔ DB
    • 요구사항에만 집중하여 개발할 수 있도록!
      • 반복되고 실수가 많은 부분 → 스프링이 대신~!!
    • 스프링은 개발 편의를 증대
      • 기업의 요구사항에만 집중

스프링 프레임워크를 통해 개발자는 비즈니스 로직만 신경쓰면 된다.

0개의 댓글