
오늘의 실습 목표는 "회원 관리 기능 만들어보기!" 입니다.
본 게시글에서 다루는 사항은 회원 관리 기능의 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 테스트하기

댓글