SKKUNION - Service, Controller API 형태로 개발하기(미완성)
Service 개발
Service는 이제 실제로 로그인 구현이 돌아가는 장소입니다. 일단 회원가입 관련 로직으로 지난번에 아래 이미지에 나온 JSON 타입으로 데이터를 넘겨주기로 했었어요.
그래서 이걸 파싱하고 저장하는 데이터 타입 클래스로 SignUpDto를 생성했고요.
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SignUpDto {
private String userEmail;
private String userPassword;
private String userName;
private String userPhoneNumber;
}
이제 Service에서 SignUp 부분을 만들어봅시다. 아래 코드에서 @Autowired는 스프링 빈, 컨테이너에 대해서 배우셔야지 이해가 될 거에요. 간단하게 말하면 Spring의 설계는 SOLID 원칙중 DIP(Dependency Inversion Principle)을 지키기 위해서 Config가 Service 내부의 Repository, Controller 내부의 Repository와 Service의 의존성을 관리하게 설계가 되어있는데 해당 설계를 위한 컨테이너가 스프링 빈입니다.
@Service
public class LoginService {
// @Autowired // 이 부분은 스프링 빈 내부에 등록된 LoginRepository를 사용하겠다는 의미입니다.
// LoginRepository loginRepository; // 자동으로 = new LoginRepository()를 스프링 빈 내부에서 가져와서 해줘요.
// 위처럼 하는 코드 스타일도 있는데 문제가 Test할 때에 안 좋은 점도 있고
// 매번 번거로워서 해당 코드로 변경합니다. 지금은 이게 번거롭지만 선언되는 게 많아질수록
// 이게 더 덜 번거로울거에요.
private LoginRepository loginRepository;
@Autowired
public LoginService(LoginRepository loginRepository) {
this.loginRepository = loginRepository;
}
public ResponseDto<?> signUp(SignUpDto dto){
String userEmail = dto.getUserEmail();
String userPassword = dto.getUserPassword();
String userName = dto.getUserName();
String userPhoneNumber = dto.getUserPhoneNumber();
// email이 DB 내부에 있는지만 확인
UserEntity userEntity = loginRepository.findByUserEmail(userEmail);
if(userEntity == null){
System.out.println("it is not existed");
try{
// id는 pk인데 데이터베이스에서 관리하는 키 값이기 때문에 null로 생성
// set~~로 해도 되는데 보기에도 좋지 않고 좋은 선택은 아님.
// 이미 영속성 컨텍스트 내부에 있는 값을 set으로 변경하면 그거 호출마다 업데이트 쿼리가 자동으로 생성돼서 겁나 날라감.
userEntity = new UserEntity(null, userEmail, userName, userPassword, userPhoneNumber);
// save는 데이터 베이스에 저장하는 것을 의미함.
loginRepository.save(userEntity);
} catch(Exception e){
System.out.println("database Error");
}
} else{
System.out.println("it is already existed");
return ResponseDto.setSuccess("SignUp Failed", null);
}
return ResponseDto.setSuccess("SignUp Success!", dto);
}
}
signUp에서는 SignUpDto 내부 값을 받아서 확인을 해줘요. 우선 DB 내부에 이미 등록된 email과 동일한 email이 있으면 안되니까 DB에서 user_email 컬럼 값이 똑같은 것 만을 선택해서 가져와요. 그러니까 select 쿼리가 날라가죠. 그러면 정상적으로 실행을 했을 때 해당 로직에서 아래와 같은 쿼리가 날라가고 찾은 게 있으면 null이 아니고 해당하는 userEntity를 생성자를 통해 생성해줍니다.
나머지 부분은 주석을 보시면 잘 이해가 될 것이라고 생각하고 넘길게요. save는 데이터베이스 내부에 해당하는 Entity 값을 저장하는 것을 의미해요. 그래서 해당 호출에서 제대로 타입만 입력했다면 문제없이 아래의 쿼리가 날라갈 겁니다.
이제 http에서 날라오기전에 Test를 작성하실 수도 있는데 맥은 단축키를 모르겠지만 윈도우에서는 인텔리제이에서 signUp메서드에서 shift + ctrl + T버튼을 누르면 테스트 코드.java를 자동으로 만들어줘요.
저 같은 경우에는 Test -> service -> LoginServiceTest가 자동으로 생성이 됐네요. 여기 내부를 구현을 해야하는데 LoginService 중 SignUp이 제대로 돌아가는건지 확인을 해야하니 Test를 작성해봅시다.
---- 이 부분은 제대로 공부를 못해서 다음에 작성하겠습니다. ------
이제 Test에서 정상적으로 Service에서 작동하는 것을 확인을 했으면, Controller에서 클라이언트에서 보내는 데이터와 연결을 해주어야 되는데 아래처럼 코드를 작성하면 됩니다.
@RestController는 JSON 타입의 데이터를 받아올 때 사용하고 @RequestMapping은 "/api/login"을 담당하는데 아래에 @PostMapping이나 @GetMapping 등으로 뒤에 어떤 http 메서드에 어떻게 연결할지를 결정할 수 있어요.
@RequestBody라는 걸 입력하면 해당 SignUpDto의 내부와 JSON 타입의 로직이 파싱가능하게 매칭이 되면 자동으로 Dto 인스턴스 형태로 바꿔주기 때문에 해당 내용을 바로 사용하실 수 있습니다. 그러면 이제 result에 요청에 따라서 이전에 service에 대해서 다른 값이 뜰거에요.
@RestController
@RequestMapping("/api/login")
public class LoginController {
// @Autowired
// LoginService loginService;
private LoginService loginService;
@Autowired
public LoginController(LoginService loginService) {
this.loginService = loginService;
}
@PostMapping("/signUp")
public ResponseDto<?> signUp(@RequestBody SignUpDto requestBody){
ResponseDto<?> result = loginService.signUp(requestBody);
return result;
}
}
이전에 올린 부분을 잘 따라와 주셨다면 다음과 같이 뜰거고 실제로 Login 정보를 가려야하니 실제로 보낼 때에는 Data 부분이 null로 들어갈겁니다.
DB 내부도 한번 잘 들어갔는지 확인을 해야하니까. 확인을 직접 해보면 다음과 같이 뜹니다.