Annotation
스프링 부트는 어노테이션을 다양하게 아는 것이 중요하다. SpringBoot의 시작점을 알리는 @SpringBootApplication 어노테이션 뿐만 아니라 스프링 부트 어노테이션 등의 키워드로 구글링 해서 스프링 부트에서 자주 사용되는 다양한 어노테이션을 이해하고 외워두자.
package com.example.demo.src.user.model;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
@Getter // 해당 클래스에 대한 접근자 생성
@Setter // 해당 클래스에 대한 설정자 생성
@AllArgsConstructor // 해당 클래스의 모든 멤버 변수(userIdx, nickname, email, password)를 받는 생성자를 생성
/**
* Res.java: From Server To Client
* 하나 또는 복수개의 회원정보 조회 요청(Get Request)의 결과(Respone)를 보여주는 데이터의 형태
*
* GetUserRes는 클라이언트한테 response줄 때 DTO고
* User 클래스는 스프링에서 사용하는 Objec이다.
*/
public class GetUserRes1 {
//private int user_idx;
private String user_email;
}
package com.example.demo.src.user.model;
import lombok.*;
@Getter // 해당 클래스에 대한 접근자 생성
@Setter // 해당 클래스에 대한 설정자 생성
@AllArgsConstructor // 해당 클래스의 모든 멤버 변수(userIdx, nickname)를 받는 생성자를 생성
@NoArgsConstructor(access = AccessLevel.PROTECTED) // 해당 클래스의 파라미터가 없는 생성자를 생성, 접근제한자를 PROTECTED로 설정.
/**
* Req.java: From Client To Server
* 회원정보 수정 요청(Patch Request)을 하기 위해 서버에 전달할 데이터의 형태
*/
public class PatchUserReq {
private String user_email;
private String user_password;
}
package com.example.demo.src.user.model;
import lombok.*;
@Getter // 해당 클래스에 대한 접근자 생성
@Setter // 해당 클래스에 대한 설정자 생성
@AllArgsConstructor // 해당 클래스의 모든 멤버 변수(userIdx, nickname, email, password)를 받는 생성자를 생성
@NoArgsConstructor(access = AccessLevel.PROTECTED)
/**
* 유저관련된 정보들을 담고 있는 클래스(유저 관련정보를 추출할 때 해당 클래스에서 Getter를 사용해서 가져온다.)
* GetUserRes는 클라이언트한테 response줄 때 DTO고(DTO란 DB의 정보를 Service나 Controller등에 보낼때 사용하는 객체를 의미한다.)
* User 클래스는 스프링에서 사용하는 Objec이다.(내부에서 사용하기 위한 객체라고 보면 된다.)
*/
public class User {
private int user_idx;
private String user_email;
private String user_password;
private String user_status;
}
아이디찾기(이메일 조회)api로 문자인증에서 사용되는 전화번호를 파라미터로 받는다.
/**
* 이메일조회 API
* [GET] /users/:user_phone
*/
// Path-variable
@ResponseBody
@GetMapping("/{user_phone}") // (GET) 127.0.0.1:9000/app/users/:userIdx
public BaseResponse<GetUserRes1> getUser(@PathVariable("user_phone") String user_phone) {
try {
GetUserRes1 getUserRes = userProvider.getUser(user_phone);
return new BaseResponse<>(getUserRes);
} catch (BaseException exception) {
return new BaseResponse<>((exception.getStatus()));
}
}
- 문자 인증 시 필요한 전화번호나 이메일에대한 형식적validation과 의미적validation은 문자인증(fcm)api에서 구현해줘야하나? 아님 회원정보 조회 및 변경api에서 구현해줘야하나?
👉 회원정보api에서 구현하면 문자인증이 필요한 api마다 validation처리를 해줘야하기 때문에 나는 문자인증api에 구현하였다.- 비밀번호 변경이 문자 인증 후에 이루어지는데, 그러면 비밀번호 변경api랑 문자인증api를 컨트롤러상에서 연결시켜줘여하나.?아님 그냥 각각따로 만들면 되나?
👉 api는 따로따로 있다고 보면 된다.
회원가입 때와 마찬가지로, 비밀번호 형식에대한 validation이 필요하다.
/**
* 비밀번호변경 API
* [PATCH] /users/:user_email
*/
@ResponseBody
@PatchMapping("/{user_email}")
public BaseResponse<String> modifyUserPassword(@PathVariable("user_email") String user_email, @RequestBody User user) {
/*if(!isRegexEmail(user_email)){
return new BaseResponse<>(POST_USERS_INVALID_EMAIL);
}*/
if (user.getUser_password() == null) {
return new BaseResponse<>(POST_USERS_EMPTY_PW);
}
if(!isRegexPw(user.getUser_password())){
return new BaseResponse<>(POST_USERS_INVALID_PW);
}
try {
PatchUserReq patchUserReq = new PatchUserReq(user_email, user.getUser_password());
userService.modifyUserPassword(patchUserReq);
String result = "회원정보가 수정되었습니다.";
return new BaseResponse<>(result);
} catch (BaseException exception) {
return new BaseResponse<>((exception.getStatus()));
}
}
비즈니스 로직을 다루는 곳 (DB 접근[CRUD], DB에서 받아온 것 형식화)
-> Request의 의미적 Validation 처리 (DB를 거쳐야 검사할 수 있는)
Service와 Provider는 비즈니스 로직을 다루는 곳이다. CRUD 중 R(Read) 에 해당하는 코드가 긴 경우가 많기 때문에 R(Read) 만 따로 분리해 Service는 CUD(Create, Update, Delete) 를, Provider는 R(Read) 를 다루도록 한다. 유지 보수가 용이해진다.
1. Provider
R(Read) 와 관련된 곳이다. DB에서 select 해서 얻어온 값을 가공해서 뱉어준다.
2. Service
CUD(Create, Update, Delete) 와 관련된 곳이다. CUD에서 R이 필요한 경우가 있는데, 그럴 때는 Provider에 구성되어 있는 것을 Service에서 사용하면 된다.
// 해당 user_phone를 갖는 User의 정보 **조회**
public GetUserRes1 getUser(String user_phone) throws BaseException {
try {
GetUserRes1 getUserRes = userDao.getUser(user_phone);
return getUserRes;
} catch (Exception exception) {
throw new BaseException(DATABASE_ERROR);
}
}
db상에 회원email이 존재하는지 확인하는 메소드
public int checkEmail(String user_email) throws BaseException {
try{
return userDao.checkEmail(user_email);
} catch (Exception exception) {
throw new BaseException(DATABASE_ERROR);
}
}
// 회원정보 **수정(Patch)**
public void modifyUserPassword(PatchUserReq patchUserReq) throws BaseException {
if(userProvider.checkEmail(patchUserReq.getUser_email()) == 0) {
throw new BaseException(NON_EXISTENT_EMAIL);
}
String pwd;
try {
pwd = new SHA256().encrypt(patchUserReq.getUser_password());
patchUserReq.setUser_password(pwd);
int result = userDao.modifyUserPassword(patchUserReq); // 해당 과정이 무사히 수행되면 True(1), 그렇지 않으면 False(0)입니다.
if (result == 0) { // result값이 0이면 과정이 실패한 것이므로 에러 메서지를 보냅니다.
throw new BaseException(MODIFY_FAIL_USERPASSWORD);
}
} catch (Exception exception) { // DB에 이상이 있는 경우 에러 메시지를 보냅니다.
throw new BaseException(DATABASE_ERROR);
}
}
public int checkEmail(String user_email) {
String checkEmailQuery = "select exists(select user_email from User where user_email = ?)";
String checkEmailParams = user_email;
return this.jdbcTemplate.queryForObject(checkEmailQuery,
int.class,
checkEmailParams);
}
EXISTS 절은 서브쿼리(select 구문)가 반환하는 결과값이 이미 존재하는지 조사하여, 있으면 TRUE를 반환한다.
// 해당 user_phone을 갖는 유저조회
public GetUserRes1 getUser(String user_phone) {
String getUserQuery = "select user_email from User where user_phone = ?"; // 해당 user_id를 만족하는 유저를 조회하는 쿼리문
String getUserParams = user_phone;
return this.jdbcTemplate.queryForObject(getUserQuery,
(rs, rowNum) -> new GetUserRes1(
rs.getString("user_email")),// RowMapper(위의 링크 참조): 원하는 결과값 형태로 받기
getUserParams); // 한 개의 회원정보를 얻기 위한 jdbcTemplate 함수(Query, 객체 매핑 정보, Params)의 결과 반환
}
update 쿼리문을 사용하여 테이블의 데이터를 수정한다.
UPDATE [테이블] SET [열] = '변경할값' WHERE [조건]
// 회원정보 변경
public int modifyUserPassword(PatchUserReq patchUserReq) {
String modifyUserNameQuery = "update User set user_password = ? where user_email = ? "; // 해당 user_email를 만족하는 User를 해당 nickname으로 변경한다.
Object[] modifyUserNameParams = new Object[]{patchUserReq.getUser_password(), patchUserReq.getUser_email()}; // 주입될 값들(password, user_idx) 순
return this.jdbcTemplate.update(modifyUserNameQuery, modifyUserNameParams); // 대응시켜 매핑시켜 쿼리 요청(생성했으면 1, 실패했으면 0)
}
/users/:user_phone
/users/:user_email