[Spring] Spring Java에서 활용할 간단한 모델 객체 만들기

thdtjdals__·2023년 11월 30일
0

Spring

목록 보기
2/11
post-thumbnail

안녕하세요!
오늘은 자바에서 활용할 모델 객체를 만들어보려고 합니다.
여기서 모델 객체란? 우리가 다뤄야 할 데이터의 범주라고 보면 됩니다.

오늘은 간단하게 상품에 관한 간단한 CRUD를 구현해보려고합니다.

자바에서 모델 객체를 만들고 이를 활용하여 간단한 상품이나 다른 모델 객체에 대해서 CRUD(Create, Read, Update, Delete)를 구현하는 것은 웹 개발에서 기본이 되는 부분 중 하나입니다. 이번 시간에는 상품 모델을 만들고, 스프링 부트 설정 파일을 통해 데이터베이스와의 연동을 설정하는 방법에 대해 자세히 알아보겠습니다.

스프링 부트 설정 파일

우선 데이터베이스와 연동하기 위한 스프링 부트 h2 데이터베이스에 맞춘 기본적인 설정을 해보겠습니다.

우선 저는 이 프로퍼터 실행 파일에 이렇게 추가하였습니다.
작성한 위 기능에 대해서 몇가지만 간단히 설명해드리겠습니다.

"spring.output.ansi.enabled=always" : ANSI 컬러 출력을 활성화하여 로그를 더 가독성 있게 표시합니다.

"logging.level.org.springframework.web=DEBUG" : Spring 웹 관련 로그의 디버그 레벨을 설정합니다.

"logging.level.org.hibernate=DEBUG" : Hibernate 관련 로그의 디버그 레벨을 설정합니다.

"spring.jpa.hibernate.ddl-auto=update" : JPA가 데이터베이스 스키마를 자동으로 생성 또는 업데이트하도록 설정합니다.

"spring.jpa.show-sql=true" : JPA가 생성하는 SQL 쿼리를 콘솔에 출력합니다.

"spring.jpa.properties.hibernate.format_sql=true" : 생성되는 SQL 쿼리를 보기 쉽게 포맷팅합니다.

"spring.h2.console.enabled=true" : H2 데이터베이스 콘솔을 활성화합니다.

"spring.datasource.url=jdbc:h2:~/test;AUTO_SERVER=true" : H2 데이터베이스의 URL을 설정합니다. 여기서 "AUTO_SERVER=true"는 여러 연결을 허용합니다.

"spring.datasource.driverClassName=org.h2.Driver" : H2 데이터베이스 드라이버를 설정합니다.

"spring.datasource.username=sa" : 데이터베이스 사용자 이름을 설정합니다.

spring.datasource.password=: 데이터베이스 비밀번호를 설정합니다.

spring.jpa.database-platform=org.hibernate.dialect.H2Dialect: Hibernate가 H2 데이터베이스를 사용할 때의 데이터베이스를 설정합니다.

엔티티 클래스 생성하기

웹 애플리케이션에서 데이터를 다루기 위해 엔티티 클래스를 만들어야 합니다. 엔티티 클래스는 데이터베이스 테이블과 일치하도록 매핑되며 여기에서는 상품 정보를 다루는 Product를 테이블 명으로 엔티티 클래스를 작성하는 방법에 대해 작성해보겠습니다.

이제 상품에 관한 간단한 CRUD(Create, Read, Update, Delete)를 구현하기 위한 엔티티 클래스를 작성하는 방법을 자세하게 알아보겠습니다.

여기서 CRUD란?
Create, Read, Update, Delete의 약자로 데이터의 생성, 조회, 수정, 삭제를 의미합니다

package com.test.SpringBootApi.domain;

import javax.persistence.*;

@Entity
@Table(name="products") //테이블의 이름을 구성
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(name = "product_name")
    private String productName;

    @Column(name = "price")
    private int price;

    public Product(){}

    public Product(String productName, int price) {
        this.productName = productName;
        this.price = price;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getProductName() {
        return productName;
    }

    public void setProductName(String productName) {
        this.productName = productName;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }
}

여기서 보이시는 "@"은 어노테이션이라 불립니다. 어노테이션은 자바 프로그래밍 언어에서 소스 코드에 메타데이터를 추가하기 위한 특별한 형식의 문법입니다. 어노테이션은 "@" 기호로 시작하며 자바 코드의 다양한 요소에 적용될 수 있습니다. 어노테이션은 클래스, 메서드, 필드 등의 선언 부분에 붙일 수 있습니다.

위 코드에서 @Entity는 JPA에서 엔티티 클래스임을 나타내는 어노테이션입니다. 이 클래스가 데이터베이스 테이블과 매핑됩니다.

@Table(name="products")이렇게 설정을 하면 테이블 이름을 products로 생성하게 됩니다. 해당 엔티티가 매핑되는 테이블의 이름을 지정하는 것이라고 보면 됩니다. 여기서는 "products" 테이블과 매핑되도록 했습니다.

@Id는 해당 필드가 데이터베이스의 기본 키(primary key)인 것을 나타냅니다.

@GeneratedValue(strategy = GenerationType.AUTO)은 기본 키 값이 자동으로 생성되도록 설정합니다. 숫자를 부여하는 규칙으로 보통 하나씩 증가하는 형태로 구성됩니다. 예를 들면 1. 2. 3. ... 이런식으로 증가하게 된다는 것입니다.


@Column(name = "product_name"), @Column(name = "price")은 해당 필드가 데이터베이스 컬럼의 이름을 지정하는 것입니다.(나중에 데이터베이스 테이블에서 보면 확실히 이해가 감)

이 부분은 기본 생성자 및 인자를 받는 생성자입니다. JPA는 엔티티를 생성할 때 기본 생성자를 필요로 하므로 두 가지 생성자를 함께 구현했습니다.

Getter 및 Setter 메서드: 각 필드의 접근자 및 설정자 메서드를 생성하여 객체의 속성을 다룰 수 있도록 합니다.

엔티티 클래스 활용(레포지토리 인터페이스)

이제 작성한 Product 엔티티 클래스를 활용해 보겠습니다. 이 클래스를 통해 데이터베이스의 "products" 테이블과 상호작용하는 레포지토리 인터페이스를 작성였습니다.

package com.test.SpringBootApi.respository;

import com.test.SpringBootApi.domain.Product;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ProductRepository extends JpaRepository<Product, Long> {
}

위와 코드와 같이 ProductRepository를 생성하여 기본적인 CRUD(Create, Read, Update, Delete) 기능을 사용할 수 있게 되었습니다. 이 인터페이스를 통해 데이터베이스와 상호작용 할 수 있게 된것입니다.

서비스 인터페이스 생성하기

서비스 인터페이스는 기능에 대한 로직을 추상화하고 기능 구현에 있어서 구체적인 동작을 정의하는 역할을 합니다.

앞서 작성한 엔티티 클래스를 기준으로 서비스 인터페이스를 작성하였습니다

package com.test.SpringBootApi.service;

import com.test.SpringBootApi.domain.Product;

import java.util.Optional;

public interface ProductService {

    public Product save(Product product);
    public Optional<Product> findById(Long id);
    public Product update(Long id, Product product);
    public void delete(Long id);

}

이 인터페이스는 상품과 관련된 다양한 서비스 메서드를 정의하도록 작성하였습니다. save, findById, update, delete 등의 메서드를 통해 서비스의 주요 기능을 추상화하고 있습니다.

save는 저장하게 되면 상품이 저장 결과를 나타내는 것입니다.

findById에서 Optional은 Null값을 주려할때 알맞게 대응해주는 객체라고 볼 수 있습니다.

update는 고유한 아이디값으로 객체값을 찾고 새로 입력한 댓글값으로 덮게되는 원리로 보면됩니다.

delete는 고유한 아이디값으로 객체를 찾고 그것을 삭제해버리는 것 입니다. 그러므로 특별한 반환값을 설정하지 않아도 됩니다.

서비스 클래스 생성하기

이제 서비스 인터페이스를 구현하는 구현체인 서비스 클래스를 작성해보겠습니다.

package com.test.SpringBootApi.service;

import com.test.SpringBootApi.domain.Product;
import com.test.SpringBootApi.respository.ProductRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    private ProductRepository productRepository;

    @Override
    public Product save(Product product) {
        try {
            return productRepository.save(new Product(product.getProductName(), product.getPrice()));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public Optional<Product> findById(Long id) {
        try {
            Optional<Product> productData = productRepository.findById(id);
            if (productData.isPresent()) {
                return productData;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Optional.empty();
    }

    @Override
    public Product update(Long id, Product product) {
        try {
            Optional<Product> productData = productRepository.findById(id);
            if (productData.isPresent()) {
                Product _product = productData.get();
                _product.setProductName(product.getProductName());
                _product.setPrice(product.getPrice());
                productRepository.save(_product);
                return _product;
            } else {
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public void delete(Long id) {
        try {
            productRepository.deleteById(id);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

각 코드에 대한 설명은 스프링에서 아래의 제가 작성한 코드 사진들로 대체하겠습니다.


상품 데이터 저장(Create)


상품 데이터 조회(Read)

상품 데이터 수정(Update)

상품 데이터 삭제(Delete)

컨트롤러 클래스 생성하기

이제 마지막으로 컨트롤러 클래스 입니다. 스프링 부트에서는 컨트롤러가 사용자의 HTTP 요청을 받아 비즈니스 로직을 실행하고 응답을 반환하는 핵심 역할을 합니다.

package com.test.SpringBootApi.controller;

import com.test.SpringBootApi.service.ProductServiceImpl;
import com.test.SpringBootApi.domain.Product;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Optional;

@CrossOrigin("*")
@RestController
@RequestMapping("/api")
public class ProductController {

    @Autowired
    ProductServiceImpl productService;

    @GetMapping("/products/{id}")
    public ResponseEntity<Optional<Product>> getProductById(@PathVariable("id") long id) {
        try{
            return ResponseEntity.ok(productService.findById(id));
        } catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    @PostMapping("/products")
    public ResponseEntity<Product> createProduct(@RequestBody Product product) {
        try{
            return ResponseEntity
                    .status(HttpStatus.CREATED)
                    .body(productService.save(product));
        } catch(Exception e){
            e.printStackTrace();
        }
        return null;
    }

    @PutMapping("/products/{id}")
    public ResponseEntity<Product> updateProduct(
            @PathVariable("id") long id,
            @RequestBody Product product
    ) {
        try{
            return ResponseEntity
                    .status(HttpStatus.CREATED)
                    .body(productService.update(id, product));
        } catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    @DeleteMapping("/products/{id}")
    public ResponseEntity<HttpStatus> deleteProduct(@PathVariable("id") long id) {
        try{
            productService.delete(id);
            return ResponseEntity.noContent();
        } catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
}

@CrossOrigin("*") : 요청의 주제가 특정 도메인을 제안하는 부분입니다. 전체를 허용한다는 뜻이며 보안때문에 설정하였습니다.

@RestController : 사용자 입력을 http방식에 어떠한 경로에 맞춰서 호출하면 거기에 맞춰서 각각의 서비스를 호출하도록 구성이된 어노테이션입니다.

@RequestMapping("/api") : 어떤 경로로 사용자의 요청을 받을지 메인 주소 옆에다 /api이걸 붙혀줘야합니다.

실제로 서비스의 명령을 내리기 위해서는 사용자의 요청을 직접적으로 받는 부분이 필요한데 그 부분이 바로 여기 컨트롤러입니다. 컨트롤러는 서비스를 찾고 서비스는 레포지토리를 찾고 이런 방식이라고 보면 됩니다. 컨트롤러는 사용자의 입력을 받아서 서비스한테 시키는 역할을 하는 애라고 보면 편합니다.

상품 데이터 저장(Create)


상품 데이터 조회(Read)

상품 데이터 수정(Update)

상품 데이터 삭제(Delete)

프로젝트 실행

코드를 모두 작성하고 실행을 했을시 실행창에 위와같이 떴을 경우 성공!

지금까지 작성한 코드들로 Postman을 사용하여 실제로 상품을 등록해보겠습니다.

Postman을 열고 HTTP POST 요청을 보낼 컨트롤러 클래스에서 작성한 주소를 입력한 뒤 Body 탭으로 이동하고 raw를 선택한 후 JSON 형식으로 위와 같이 데이터를 입력하고 Send 버튼을 클릭하여 요청을 보내면 됩니다.

200 OK 가 떴다면 데이터를 추가하는데 성공!

이제 전에 포스팅 했던 웹에서 localhost:8080으로 들어가봅시다.

설정하신 유저 이름과 비밀번호를 입력 후 connect를 누릅니다.


저렇게 저희가 입력한 데이터가 뜬다면 성공!

이제 우리는 Spring Boot API를 사용하여 성공적으로 상품을 등록한 모습을 볼 수 있습니다. 이제 이 API를 통해 조회, 변경, 삭제도 가능할 것입니다.

오늘은 여기까지입니다.

감사합니다!

사진 출처 : 구글 이미지

profile
내가 보려고 만든 공부, 개발 정리

0개의 댓글