본문 바로가기
Programming/Spring Boot

[실습] 스프링부트(Spring Boot)로 회원 관리 기능 구현하기 - REST API 구현하기

by 돌방로그 2023. 2. 14.


오늘의 실습 목표는 "회원 관리 기능 만들어보기!" 입니다.
본 게시글에서 다루는 사항은 회원 관리 기능의 Backend 단인 API를 구현하는 과정입니다.


회원 관리 구현하기 - API (Contoller,  Service, Repository, Domain)

사전 준비

아래 사항에 대해서 사전 준비가 완료되지 않으신 분들은 아래 링크를 참조하여 사전 준비를 진행합니다.

 

혹시라도 아래 개념이 잘 기억나지 않으시는 분들은 관련 링크를 참조하시기 바랍니다.

 

 

파일 구조

테이블을 생성하고 CRUD 기능을 테스트하기 위해서 작업해야 할 파일은 총 4개입니다.

아래 폴더/파일 트리 구조와 호출 흐름도는 전체 구현이 완료된 후 갖춰질 구조입니다.

작업할 파일에 대한 간략한 설명은 아래를 참고하시면 됩니다.

  • AdminUserInformation
    • 목적: AdminUserInformation 테이블과 매핑되는 객체 정의 파일
    • 경로: src\main\java\...\user\domain
  • AdminUserInformationRepository
    • 목적: AdminUserInformation 에 대한 메소드 정의 파일
    • 경로: src\main\java\...\user\repository
  • AdminUserInformationService
    • 목적: REST API의 호출 결과를 처리하기 위한 비즈니스 로직이 담겨져 있는 파일
    • 경로: src\main\java\...\user\service
  • AdminUserInformationController
    • 목적: FE(Front-end)의 뷰와 연결되는 최상위 파일로 REST API와 매핑되는 함수가 구현되어 있는 파일
    • 경로: src\main\java\...\user\controller

테이블 구조

[실습] 스프링부트(Spring Boot)로 회원 관리 기능 구현하기 - DB, API 기획/설계하기 를 참조해주세요.

 


소스 코드

AdminUserInformation

위 '테이블 구조'에 링크된 페이지에서 정의한 바와 같이 각 컬럼의 타입과 길이, 속성을 지정합니다.

package com.logsjejustone.webapiserver.user.domain;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.validation.constraints.NotNull;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.time.LocalDateTime;

@Getter
@Setter
@NoArgsConstructor
@Entity
public class AdminUserInformation {

    @Id
    @NotNull
    @Column(length = 6)
    private String employeeNo;

    @NotNull
    @Column(length = 6)
    private String registerEmployeeNo;

    @NotNull
    private LocalDateTime registerDatetime;

    @NotNull
    @Column(length = 6)
    private String updateEmployeeNo;

    @NotNull
    private LocalDateTime updateDatetime;

    @NotNull
    @Column(length = 20)
    private String employeeName;

    @Column(length = 50)
    private String employeeNameEng;

    @NotNull
    @Column(length = 20)
    private String employeeCompany;

    @NotNull
    @Column(length = 20)
    private String employeeDivision;

    @NotNull
    @Column(length = 20)
    private String employeeTeam;

    @Column(length = 10)
    private String employeePosition;

    @NotNull
    @Column(length = 50)
    private String employeeEmail;

    @NotNull
    @Column(length = 13)
    private String employeePhone;

    @NotNull
    @Column(columnDefinition = "TEXT")
    private String employeePw;

    @NotNull
    @Column(length = 8)
    private String employeePwExpDate;

    @NotNull
    @Column(length = 8)
    private String usageExpDate;
}

 

AdminUserInformationRepository

JpaRepository를 상속받아 기본적인 메소드들은 별도로 정의하지 않고 이용할 수 있습니다.

기본적으로 사용할 수 있는 메소드는 'JpaRepository' 링크를 참조해주세요.

package com.logsjejustone.webapiserver.user.repository;

import com.logsjejustone.webapiserver.user.domain.AdminUserInformation;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.List;

public interface AdminUserInformationRepository extends JpaRepository<AdminUserInformation, String> {
    List<AdminUserInformation> findByUsageExpDateGreaterThanEqual(String usageExpDate);
}
  • 기본적으로 제공하지는 않지만 JPA Query Creation 기능을 이용하여 현재 쿼리 조회 시점 일자가 사용 기한 일자(usageExpDate)보다 큰 데이터에 한해서만 추출될 수 있도록 API를 추가하였습니다.

 

AdminUserInformationService

비즈니스 로직을 처리하는 구문으로 테이블에서 데이터를 가져오기 전과 가져온 후에 대한 처리를 하는 코드입니다.

전체 코드가 궁금하신 분들은 아래 더보기를 클릭하여 확인해주시길 바랍니다.

더보기
package com.logsjejustone.webapiserver.user.service;

import com.logsjejustone.webapiserver.user.domain.AdminUserInformation;
import com.logsjejustone.webapiserver.user.repository.AdminUserInformationRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Optional;

@Service
public class AdminUserInformationService {

    @Autowired
    private AdminUserInformationRepository adminUserInformationRepository;

    private final LocalDateTime localDateTime = LocalDateTime.now();

    // CREATE
    public ResponseEntity<AdminUserInformation> AddAdminUser(AdminUserInformation adminUserInformation) {
        ResponseEntity<AdminUserInformation> responseEntity;
        Optional<AdminUserInformation> optAdminUserInfo = this.adminUserInformationRepository.findById(adminUserInformation.getEmployeeNo());

        if(optAdminUserInfo.isPresent()) {
            System.out.println("[ERROR/AddAdminUser] The employee No(" + adminUserInformation.getEmployeeNo() + ") already exists.");

            responseEntity = new ResponseEntity<>(HttpStatus.CONFLICT);
        }
        else {
            adminUserInformation.setRegisterDatetime(localDateTime);
            adminUserInformation.setUpdateDatetime(localDateTime);

            this.adminUserInformationRepository.save(adminUserInformation);

            responseEntity = new ResponseEntity<>(HttpStatus.OK);
        }
        return responseEntity;
    }

    // READ
    public List<AdminUserInformation> GetAllAdminUsers() {
        return this.adminUserInformationRepository.findAll();
    }

    public List<AdminUserInformation> GetAvailableAdminUsers() {
        String usageExpDate = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        System.out.println("[AdminUserInformation:GetAvailableAdminUsers] Search Condition(usageExpDate):" + usageExpDate);
        return this.adminUserInformationRepository.findByUsageExpDateGreaterThanEqual(usageExpDate);
    }
    
    public AdminUserInformation GetSpecificAdminUser(Map<String, String> employee) {
        AdminUserInformation adminUserInformation;
        String employeeNo = employee.get("employeeNo");

        Optional<AdminUserInformation> optAdminUserInfo = this.adminUserInformationRepository.findById(employeeNo);
        if(optAdminUserInfo.isEmpty()) {
            System.out.println("[INFO/UpdateAdminUserPw] The employee No(" + employeeNo + ") does NOT exist.");
            adminUserInformation = null;
        }
        else {
            adminUserInformation = optAdminUserInfo.get();
        }

        return adminUserInformation;
    }

    // UPDATE
    public ResponseEntity<AdminUserInformation> UpdateAdminUserInformation(AdminUserInformation adminUserInformation,
                                                                           String targetEmpNo) {
        ResponseEntity<AdminUserInformation> responseEntity;
        Optional<AdminUserInformation> optAdminUserInfo = this.adminUserInformationRepository.findById(targetEmpNo);

        if(optAdminUserInfo.isEmpty()) {
            System.out.println("[ERROR/UpdateAdminUserInformation] The employee No(" + adminUserInformation.getEmployeeNo() + ") does NOT exist.");

            responseEntity = new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }
        else {
            AdminUserInformation afterAdminUserInfo = optAdminUserInfo.get();
            System.out.println("[UpdateAdminUserInformation] afterAdminUserInfo: " + afterAdminUserInfo);

            afterAdminUserInfo.setUpdateDatetime(localDateTime);
            afterAdminUserInfo.setUpdateEmployeeNo(adminUserInformation.getUpdateEmployeeNo());

            // 사용자가 변경하지 못하는 데이터
//            afterAdminUserInfo.setEmployeeName(adminUserInformation.getEmployeeName());
//            afterAdminUserInfo.setEmployeeCompany(adminUserInformation.getEmployeeCompany());

            // 사용자가 변경 가능한 데이터
            // - ROLE:1 이상
            afterAdminUserInfo.setEmployeeNameEng(adminUserInformation.getEmployeeNameEng());
            afterAdminUserInfo.setEmployeeDivision(adminUserInformation.getEmployeeDivision());
            afterAdminUserInfo.setEmployeeTeam(adminUserInformation.getEmployeeTeam());
            afterAdminUserInfo.setEmployeePosition(adminUserInformation.getEmployeePosition());
            afterAdminUserInfo.setEmployeeEmail(adminUserInformation.getEmployeeEmail());
            afterAdminUserInfo.setEmployeePhone(adminUserInformation.getEmployeePhone());

            String strCurPw = adminUserInformation.getEmployeePw();
            if(! strCurPw.equals(afterAdminUserInfo.getEmployeePw())) {
                afterAdminUserInfo.setEmployeePw(strCurPw);
                afterAdminUserInfo.setEmployeePwExpDate(localDateTime.plusDays(60).format(DateTimeFormatter.ofPattern("yyyyMMdd")));
            }

            // - ROLE:6
            afterAdminUserInfo.setUsageExpDate(adminUserInformation.getUsageExpDate());

            this.adminUserInformationRepository.save(afterAdminUserInfo);

            responseEntity = new ResponseEntity<>(HttpStatus.OK);
        }
        return responseEntity;
    }

    // DELETE
    public ResponseEntity<AdminUserInformation> DeleteAdminUser(AdminUserInformation adminUserInformation,
                                                                String targetEmpNo) {
        // * There is no action for deletion. Just update show/hide flag.
        ResponseEntity<AdminUserInformation> responseEntity;
        Optional<AdminUserInformation> optAdminUserInfo = this.adminUserInformationRepository.findById(targetEmpNo);

        if (optAdminUserInfo.isEmpty()) {
            System.out.println("[ERROR/DeleteAdminUser] The employee No(" + targetEmpNo + ") does NOT exist.");

            responseEntity = new ResponseEntity<>(HttpStatus.NOT_FOUND);
        } else {
            AdminUserInformation afterAdminUserInfo = optAdminUserInfo.get();
            System.out.println("[DeleteAdminUser] afterAdminUserInfo: " + afterAdminUserInfo);

            afterAdminUserInfo.setUpdateDatetime(localDateTime);
            afterAdminUserInfo.setUpdateEmployeeNo(adminUserInformation.getUpdateEmployeeNo());

            // 관리자(ROLE:6)가 변경 가능한 데이터
            afterAdminUserInfo.setUsageExpDate(adminUserInformation.getUsageExpDate());

            this.adminUserInformationRepository.save(afterAdminUserInfo);

            responseEntity = new ResponseEntity<>(HttpStatus.OK);
        }
        return responseEntity;
    }
}

 

CREATE

public ResponseEntity<AdminUserInformation> AddAdminUser(AdminUserInformation adminUserInformation) {
    ResponseEntity<AdminUserInformation> responseEntity;
    Optional<AdminUserInformation> optAdminUserInfo = this.adminUserInformationRepository.findById(adminUserInformation.getEmployeeNo());

    if(optAdminUserInfo.isPresent()) {
        System.out.println("[ERROR/AddAdminUser] The employee No(" + adminUserInformation.getEmployeeNo() + ") already exists.");

        responseEntity = new ResponseEntity<>(HttpStatus.CONFLICT);
    }
    else {
        adminUserInformation.setRegisterDatetime(localDateTime);
        adminUserInformation.setUpdateDatetime(localDateTime);

        this.adminUserInformationRepository.save(adminUserInformation);

        responseEntity = new ResponseEntity<>(HttpStatus.OK);
    }
    return responseEntity;
}
  • 추가할 데이터가 테이블에 존재하는 데이터인지 PK를 기준으로 확인합니다.
  • 없는 경우에만 데이터를 추가하며, 있는 경우에는 Status Code를 'CONFLICT'로 반환합니다.

 

READ

public List<AdminUserInformation> GetAllAdminUsers() {
    return this.adminUserInformationRepository.findAll();
}

public List<AdminUserInformation> GetAvailableAdminUsers() {
    String usageExpDate = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
    System.out.println("[AdminUserInformation:GetAvailableAdminUsers] Search Condition(usageExpDate):" + usageExpDate);
    return this.adminUserInformationRepository.findByUsageExpDateGreaterThanEqual(usageExpDate);
}

public AdminUserInformation GetSpecificAdminUser(Map<String, String> employee) {
    AdminUserInformation adminUserInformation;
    String employeeNo = employee.get("employeeNo");

    Optional<AdminUserInformation> optAdminUserInfo = this.adminUserInformationRepository.findById(employeeNo);
    if(optAdminUserInfo.isEmpty()) {
        System.out.println("[INFO/UpdateAdminUserPw] The employee No(" + employeeNo + ") does NOT exist.");
        adminUserInformation = null;
    }
    else {
        adminUserInformation = optAdminUserInfo.get();
    }

    return adminUserInformation;
}
  • 있는 경우에만 데이터를 조회하며, 없는 경우에는 null을 리턴합니다.
  • 함수 별 목적은 아래와 같습니다.
    • GetAllAdminUsers: 관리자 포털에 등록된 사용자 전체를 조회하는 함수입니다.
    • GetAvailableAdminUsers: 관리자 포털에 등록된 사용자 중에서 현재 사용 가능한 사용자만 조회하는 함수입니다.
    • GetSpecificAdminUser: 관리자 포털에 등록된 사용자 중 특정 사용자 한명만 조회하는 함수입니다.

 

UPDATE

public ResponseEntity<AdminUserInformation> UpdateAdminUserInformation(AdminUserInformation adminUserInformation,
                                                                       String targetEmpNo) {
    ResponseEntity<AdminUserInformation> responseEntity;
    Optional<AdminUserInformation> optAdminUserInfo = this.adminUserInformationRepository.findById(targetEmpNo);

    if(optAdminUserInfo.isEmpty()) {
        System.out.println("[ERROR/UpdateAdminUserInformation] The employee No(" + adminUserInformation.getEmployeeNo() + ") does NOT exist.");

        responseEntity = new ResponseEntity<>(HttpStatus.NOT_FOUND);
    }
    else {
        AdminUserInformation afterAdminUserInfo = optAdminUserInfo.get();
        System.out.println("[UpdateAdminUserInformation] afterAdminUserInfo: " + afterAdminUserInfo);

        afterAdminUserInfo.setUpdateDatetime(localDateTime);
        afterAdminUserInfo.setUpdateEmployeeNo(adminUserInformation.getUpdateEmployeeNo());

        // 사용자가 변경하지 못하는 데이터
//            afterAdminUserInfo.setEmployeeName(adminUserInformation.getEmployeeName());
//            afterAdminUserInfo.setEmployeeCompany(adminUserInformation.getEmployeeCompany());

        // 사용자가 변경 가능한 데이터
        // - ROLE:1 이상
        afterAdminUserInfo.setEmployeeNameEng(adminUserInformation.getEmployeeNameEng());
        afterAdminUserInfo.setEmployeeDivision(adminUserInformation.getEmployeeDivision());
        afterAdminUserInfo.setEmployeeTeam(adminUserInformation.getEmployeeTeam());
        afterAdminUserInfo.setEmployeePosition(adminUserInformation.getEmployeePosition());
        afterAdminUserInfo.setEmployeeEmail(adminUserInformation.getEmployeeEmail());
        afterAdminUserInfo.setEmployeePhone(adminUserInformation.getEmployeePhone());

        String strCurPw = adminUserInformation.getEmployeePw();
        if(! strCurPw.equals(afterAdminUserInfo.getEmployeePw())) {
            afterAdminUserInfo.setEmployeePw(strCurPw);
            afterAdminUserInfo.setEmployeePwExpDate(localDateTime.plusDays(60).format(DateTimeFormatter.ofPattern("yyyyMMdd")));
        }

        // - ROLE:6
        afterAdminUserInfo.setUsageExpDate(adminUserInformation.getUsageExpDate());

        this.adminUserInformationRepository.save(afterAdminUserInfo);

        responseEntity = new ResponseEntity<>(HttpStatus.OK);
    }
    return responseEntity;
}
  • 수정할 데이터가 테이블에 존재하는 데이터인지 PK를 기준으로 확인합니다.
  • 있는 경우에만 데이터를 수정하며, 없는 경우에는 Status Code를 'NOT FOUND'로 반환합니다.

 

DELETE

public ResponseEntity<AdminUserInformation> DeleteAdminUser(AdminUserInformation adminUserInformation,
                                                            String targetEmpNo) {
    // * There is no action for deletion. Just update show/hide flag.
    ResponseEntity<AdminUserInformation> responseEntity;
    Optional<AdminUserInformation> optAdminUserInfo = this.adminUserInformationRepository.findById(targetEmpNo);

    if (optAdminUserInfo.isEmpty()) {
        System.out.println("[ERROR/DeleteAdminUser] The employee No(" + targetEmpNo + ") does NOT exist.");

        responseEntity = new ResponseEntity<>(HttpStatus.NOT_FOUND);
    } else {
        AdminUserInformation afterAdminUserInfo = optAdminUserInfo.get();
        System.out.println("[DeleteAdminUser] afterAdminUserInfo: " + afterAdminUserInfo);

        afterAdminUserInfo.setUpdateDatetime(localDateTime);
        afterAdminUserInfo.setUpdateEmployeeNo(adminUserInformation.getUpdateEmployeeNo());

        // 관리자(ROLE:6)가 변경 가능한 데이터
        afterAdminUserInfo.setUsageExpDate(adminUserInformation.getUsageExpDate());

        this.adminUserInformationRepository.save(afterAdminUserInfo);

        responseEntity = new ResponseEntity<>(HttpStatus.OK);
    }
    return responseEntity;
}
  • 삭제 처리할 데이터가 테이블에 존재하는 데이터인지 PK를 기준으로 확인합니다.
  • 있는 경우에만 데이터를 삭제 처리(사용 기한 업데이트)하며, 없는 경우에는 Status Code를 'NOT FOUND'로 반환합니다.

 

AdminUserInformationController

테이블에 대한 처리 로직을 호출하는 REST API가 매핑되는 코드입니다.

총 5개 API가 구현되어 있습니다.

  • CREATE: (POST: ~/admin-web/users)
  • READ: (GET: ~/admin-web/users/all  ||  ~/admin-web/users/available)
  • UPDATE: (PUT: ~/admin-web/users/update/{targetEmpNo})
  • DELETE: (PUT: ~/admin-web/users/delete/{targetEmpNo})

 

아래는 전체 코드입니다.

package com.logsjejustone.webapiserver.user.controller;

import com.logsjejustone.webapiserver.user.domain.AdminUserInformation;
import com.logsjejustone.webapiserver.user.service.AdminUserInformationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/admin-web/users")
@CrossOrigin(origins = "http://localhost:8081")
public class AdminUserInformationController {

    @Autowired
    private AdminUserInformationService adminUserInformationService;

    // CREATE
    @PostMapping("")
    public ResponseEntity<AdminUserInformation> AddAdminUser(@RequestBody AdminUserInformation adminUserInformation) {
        System.out.println("[AdminUserInformation:AddAdminUser]" + adminUserInformation);

        return this.adminUserInformationService.AddAdminUser(adminUserInformation);
    }


    // READ
    @GetMapping("/all")
    public List<AdminUserInformation> GetAllAdminUsers() {
        System.out.println("[AdminUserInformation:GetAllAdminUsers]");

        return this.adminUserInformationService.GetAllAdminUsers();
    }

    @GetMapping("/available")
    public List<AdminUserInformation> GetAvailableAdminUsers() {
        System.out.println("[AdminUserInformation:GetAvailableAdminUsers]");

        return this.adminUserInformationService.GetAvailableAdminUsers();
    }
    
    @GetMapping("")
    public AdminUserInformation GetSpecificAdminUser(@RequestBody Map<String, String> employee) {
        System.out.println("[AdminUserInformation:GetSpecificAdminUser]");

        return this.adminUserInformationService.GetSpecificAdminUser(employee);
    }

    // UPDATE
    @PutMapping("/update/{targetEmpNo}")
    public ResponseEntity<AdminUserInformation> UpdateAdminUserInformation(@RequestBody AdminUserInformation adminUserInformation,
                                                                           @PathVariable String targetEmpNo) {
        System.out.println("[AdminUserInformation:UpdateAdminUserInformation]" + adminUserInformation + "\n Target:" + targetEmpNo);

        return this.adminUserInformationService.UpdateAdminUserInformation(adminUserInformation, targetEmpNo);
    }

    // DELETE
    @PutMapping("/delete/{targetEmpNo}")
    public ResponseEntity<AdminUserInformation> DeleteAdminUser(@RequestBody AdminUserInformation adminUserInformation,
                                                                @PathVariable String targetEmpNo) {
        System.out.println("[AdminUserInformation:DeleteAdminUser]" + adminUserInformation + "\n Target:" + targetEmpNo);

        return this.adminUserInformationService.DeleteAdminUser(adminUserInformation, targetEmpNo);
    }

}

 


테스트

테스트는 별도의 포스팅으로 작성해둔 아래 링크를 참조해주세요.

[실습] 스프링부트(Spring Boot)로 회원 관리 기능 구현하기 - REST API 테스트하기

 

 


댓글