본문 바로가기
[ JAVA ]/JAVA RESTful API

[ RESTful API ] JPA 연동 - 사용자 CRUD 기능 구현

by 환이s 2023. 8. 2.


이전 포스팅까지 RESTful API 기반으로 사용자 CRUD 기능 구현을 해보았습니다.

오늘은 ORM JPA를 연동해서 데이터 맵핑을 해보겠습니다.

 

  • 개발 환경
  • Spring Boot
  • JPA
  • MySQL

JPA 란?

 

JPA(Java Persistence API)는 자바에서 제공하는 ORM(Object-Relational Mapping) 기술의 표준 인터페이스입니다.

 

ORM은 객체 지향 프로그래밍과 관계형 데이터베이스 간의 매핑을 자동화하여 객체를 데이터베이스에 저장하고 조회할 수 있도록 도와주고, 객체 지향 프로그래밍에서는 클래스와 객체를 사용하여 데이터와 기능을 캡슐화하고, 관계형 데이터베이스는 테이블과 레코드로 데이터를 저장합니다.

 

이 둘 간의 패러다임 불일치를 해결하기 위해 ORM은 객체와 테이블 간의 매핑을 자동으로 처리하여 개발자가 SQL 쿼리를 직접 작성하지 않아도 데이터를 관리할 수 있게 해 줍니다.

 

JPA의 특징과 장점은 다음과 같습니다.

 


1. 표준 인터페이스:

 

JPA는 자바 표준으로, 여러 ORM 프레임워크에서 지원하는 공통된 인터페이스를 제공합니다. 따라서 JPA를 사용하면 ORM 프레임워크를 변경해도 기존 코드를 수정할 필요가 없습니다.



2. 객체 지향적인 코드 작성:

 

JPA를 사용하면 SQL 쿼리를 직접 작성하지 않아도 됩니다. 대신 객체 지향적인 방식으로 데이터를 처리할 수 있으므로 코드가 간결하고 유지보수가 용이해집니다.



3. 데이터베이스 독립성:

 

JPA는 데이터베이스에 종속적이지 않으며, 데이터베이스를 변경해도 애플리케이션 코드를 수정할 필요가 없습니다.



4. 지연 로딩 및 캐싱:

 

JPA는 엔티티를 지연 로딩하여 필요한 시점에 데이터를 가져오며, 캐시를 사용하여 데이터베이스 접근 횟수를 줄일 수 있습니다.



5. 강력한 검색 기능:

 

JPA는 JPQL(Java Persistence Query Language)을 지원하여 객체를 대상으로 쿼리를 작성할 수 있습니다. 이는 SQL과 유사하지만, 객체를 기준으로 쿼리를 작성하므로 더 직관적이고 유연한 검색 기능을 제공합니다.



6. 트랜잭션 관리:

 

JPA는 트랜잭션을 관리하여 데이터베이스 작업의 일관성과 안전성을 보장합니다.

 


JPA를 사용하려면 애플리케이션에서 JPA  인터페이스를 구현하는 ORM 프레임워크를 선택해야 합니다. 

 

Maven 환경에서 진행하기 때문에 pom.xml에 라이브러리를 추가해 줍니다.

 

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

 

추가 후 메이븐 업데이트 해주고, 이제 JPA를 사용하기 위한 인터페이스를 구현해 줍니다.

 

UserRepository

 

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends JpaRepository<User,Integer> {
}

 

인터페이스를 생성하고 JpaRepository <>를 상속받아줍니다.

 

JpaRepository <>를 상속받아서 사용하면 일반적인 CRUD 작업을 수행하는 메서드들을 간단하게 구현할 수 있으며, 커스텀 쿼리를 정의하는 기능도 제공해 줍니다.

 

여러 가지 주요 특징이 있지만 이번 포스팅은 CRUD  기능 구현이기 때문에 제공해 주는 메서드인

save,

findById,

findAll,

deleteById기본적인 CRUD  메서드를 활용해서 데이터베이스와 상호작용을 할 수 있게 연결해줍니다.

 

위 코드에서 'User'는 엔티티 클래스이며, 'Integer'는 엔티티의 기본 키 타입입니다. 

 

User

 

데이터를 전달하기 위해 객체 클래스를 생성합니다.

 




import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.validation.constraints.Past;
import javax.validation.constraints.Size;
import java.util.Date;
import java.util.List;

// @JsonIgnore  // 필드에 노출이 안됨
@Data
@AllArgsConstructor
@NoArgsConstructor
//@JsonIgnoreProperties(value = {"password","ssn"}) // @JsonIgnore를 클래스 블록 처리
//@JsonFilter("UserInfo")
@Entity
@ApiModel(description = "사용자 상세 정보를 위한 도메인 객체")
public class User {

    @Id
    @GeneratedValue
    private Integer id;
    @Size(min = 2, message = "Name은 2글자 이상 입력해 주세요.")
    @ApiModelProperty(notes = "사용자 이름을 입력해 주세요.")
    private String name;
    @Past
    @ApiModelProperty(notes = "등록일을 입력해 주세요.")
    private Date joinDate;
    @ApiModelProperty(notes = "패스워드를 입력해 주세요.")
    private String password;
    @ApiModelProperty(notes = "주민번호를 입력해 주세요.")
    private String ssn;

    @OneToMany(mappedBy = "user") // user 테이블과 매핑되게 해준다
    private List<Post> posts;


    public User(int id, String name, Date joinDate, String password, String ssn) {
        this.id = id;
        this.name = name;
        this.joinDate = joinDate;
        this.password =password;
        this.ssn = ssn;
    }
}

 

 

UserJpaController

 

@RestController
@RequestMapping("/jpa")
public class UserJpaController {

    @Autowired
    private UserRepository userRepository;

    @GetMapping("/users")
    public List<User> retrieveAllUsers(){
        return userRepository.findAll();
    }
    @GetMapping("/users/{id}")
    public EntityModel<User> retrieveUser(@PathVariable int id){
        Optional<User> user = userRepository.findById(id);

        if (!user.isPresent()){ // 데이터가 없다면?
            throw new UserNotFoundException(String.format("ID[%s] not found" , id));
        }

        EntityModel<User> model = EntityModel.of(user.get());
        WebMvcLinkBuilder linkTo = linkTo(methodOn(this.getClass()).retrieveAllUsers());
        model.add(linkTo.withRel("all-users"));

        return model;
    }

    @DeleteMapping("/users/{id}")
    public void deleteUser(@PathVariable int id){
        userRepository.deleteById(id);
    }

    @PostMapping("/users")
    public ResponseEntity<User> createUser(@Valid @RequestBody User user){
        User saveUser = userRepository.save(user);

       URI location = ServletUriComponentsBuilder.fromCurrentRequest()
                .path("/{id}")
                .buildAndExpand(saveUser.getId())
                .toUri();

       return ResponseEntity.created(location).build();
    }

 

생성한 UserRepository 클래스를 Controller와 의존성 주입을 해주고, 이전 포스팅에서는 service와 연결해서  CRUD 작업 요청을 보냈다면, 이번에는 UserRepository 클래스에 해당 매개변수와 함께 요청을 보내줍니다.

 

위 코드를 실행했을 때 사용자의 전체 정보, 상세 정보, 추가, 삭제 기능이 정상적으로 실행되는지 확인해 봅니다.

 


실행 결과

 

PostMan을 활용해서 결과를 알아보겠습니다.

 

[ MySQL 화면 ]

 

User 테이블에는 3개의 데이터가 들어가 있습니다. 

그렇다면 해당 경로마다 결과 값을 확인해 보겠습니다.


[ /users ] ( 사용자 전체 정보 )

 


[ /users/{id} ] ( 해당 사용자 정보 )

 


[ /users ] (POST) ( 사용자 추가 )

 

 

추가적으로 MySQL 환경에서도 확인해 보겠습니다.

 


[ /users/{id} ] (DELETE) ( 사용자 삭제 )

 

 


마치며

 

오늘은 ORM을 연동하여 사용자 CRUD 기능에 대해 알아봤습니다.

다음 포스팅에서 뵙겠습니다.

728x90