728x90
-1. 로그인을 할 수 있는 여러가지 방식과 그에 따른 장단점을 분석해보는 작업을 하고 있다.
-2. 공통 로그인 서비스 구현.
- User에 관련한 Entity, Dto, Repository, Service, Controller, View 등등을 구현한다.
- 회원가입 서비스의 기본적인 로직.
- User (클라이언트) 의 입력
- Id
- Password
- PasswordCheck
- Nickname
- Frontend
- Id, Nickname 중복 체크및 Data 적합성 판단
- Backend 에 Data 전달.(Id,Password,Nickname)
- Backend
- DB Connection
- Id, Nickname 중복 체크 <- 중봅 작업 이라고 생각할 수 있지만, 양쪽 단에서 체크 하는 것이 더 좋다고 들었습니다...
- 유저 정보 저장후 성공 여부 전송
- DB Disconnection
- Frontend
- 전송된 성공 여부에 관한 페이지 출력
2-1 . 설계및 코드 구현.
-1. User Entity 설계.
User Table :
- id(id, notnull,long)
- loginId(notnull, String,unique) // 로그인을 위한 id
- Password(notnull, String)
- Nickname(notnull,String,unique)
- USER_ROLE(notnull, String, "USER or ADMIN")
추가 명세서 :
- id: index 를위한 sql ID.
- loginId : 영문, 숫자로 이루어진 5~12자 이내 String. // 이라 했지만 일단 중복만..
- Password : 영문, 숫자, 특수문자로 이루어진 5~18자 이내 String. // 이라 했지만 일단 중복만..
- Nickname : 한글, 영문 ,숫자, 특수문자로 이루어진 2~10자 이내 String. // 이라 했지만 일단 중복만..
__-2. [User.java]
@Entity
@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String loginId;
private String password;
private String nickname;
private UserRole role;
}
__-3. [UserRole.java]
public enum UserRole {
USER, ADMIN;
}
-4. DTO
@Getter
@Setter
@NoArgsConstructor
public class JoinRequest {
@NotBlank(message = "로그인 아이디가 비어있습니다.") // validation을 이용한 Global 에러처리.
private String loginId;
@NotBlank(message = "비밀번호가 비어있습니다.")
private String password;
private String passwordCheck;
@NotBlank(message = "닉네임이 비어있습니다.")
private String nickname;
// TODO : 비밀번호 암호화 구현 -> 시큐리티 적용 다음
public User toEntity() {
return User.builder()
.loginId(this.loginId)
.password(this.password)
.nickname(this.nickname)
.role(UserRole.USER)
.build();
}
}
- [LoginRequest.java]
```java
@Getter
@Setter
@NoArgsConstructor
public class LoginRequest {
private String loginId;
private String password;
}
어디서나 볼 수 있는 그런 LoginRequestDto 이다.
- [UserService.java]
- 코드를 작성하기전에 서비스 클래스에서 구현해야할 서비스 로직들을 생각해봤다.
- loginId 중복 체크
- nickname 중복 체크
- 패스워드 인코딩 -> 여기서 설명하는거보다, 시큐리티를 접목시켰을때 설명하는게 좋아보여서 여기선 구현하진 않았다.
- User 정보 서버에 저장하기. -> 회원가입 완료
- 회원가입 가능여부 전송하기.(에러 처리하기)
- 로그인 기능 <- 로그인 보안 기법을 사용하지 않은 Row한 로그인 상태임으로, User정보 전송하기
- Issue
- 어떻게 구현을 구현을 하던중에.. 회원가입 가능여부 전송하기. 이 에러처리를 어디서 해야할지 고민을 한번 심도 있게 해봤다.
- Controller 에서 처리하기. <- 가장 중복체크의 로직속성을 생각하면, 일반적인 방법인거 같다.
- Service에서 처리하기 <- 유지보수 및 코드 재활용을 고려한다면, 이방법이 좋은거 같지만 Controller 에서 하는거보단 처리속도가 느리다는 단점이 있다.
- 이러한 방법이 있을수 있다고 생각을 했는데, 처리 속도를 보면 Controller 단, 유지보수 + 나중에 토큰 세션 등등을 생각하면 Service에서 하는게 맞다고 생각 했다.
- 어떻게 구현을 구현을 하던중에.. 회원가입 가능여부 전송하기. 이 에러처리를 어디서 해야할지 고민을 한번 심도 있게 해봤다.
- 코드를 작성하기전에 서비스 클래스에서 구현해야할 서비스 로직들을 생각해봤다.
@Service
@Transactional
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
// Controller 에서 처리할까 Service에서 처리할까 고민하다가. 결국 Service를 선택
/**
* loginId 중복 체크
* 회원가입 기능 구현 시 사용
* 중복되면 true
*/
public boolean checkLoginIdDuplicate(String loginId) {
return userRepository.existsByLoginId(loginId);
}
/**
* nickname 중복 체크
* 회원가입 기능 구현 시 사용
* 중복되면 true
*/
public boolean checkNicknameDuplicate(String nickname) {
return userRepository.existsByNickname(nickname);
}
/**
* 회원가입 기능
* View 에서 JoinRequest(loginId, password, nickname)을 입력받아 User로 변환 후 저장
* loginId, nickname 중복 체크는 Controller에서 진행후 메세지 쿨력.
* Controller 에서 Service 를 불러와 처리하기에 결국 처리 하는곳은 Service이다. 오해 하기 금지.
*/
public void join(JoinRequest req) {
userRepository.save(req.toEntity());
}
/**
* 로그인 기능
* 화면에서 LoginRequest(loginId, password)을 입력받아 loginId와 password가 일치하면 User return
* loginId가 존재하지 않거나 password가 일치하지 않으면 null
*/
public User login(LoginRequest req) {
Optional<User> optionalUser = userRepository.findByLoginId(req.getLoginId());
// loginId와 일치하는 User가 없으면 null return
if(optionalUser.isEmpty()) {
return null;
}
User user = optionalUser.get();
// 찾아온 User의 password와 입력된 password가 다르면 null return
if(!user.getPassword().equals(req.getPassword())) {
return null;
}
return user;
}
/**
* userId(Long)를 입력받아 User을 return 해주는 기능
* 인증, 인가 시 사용
* userId가 null이거나(로그인 X) userId로 찾아온 User가 없으면 null
* userId로 찾아온 User가 존재하면 User return
*/
public User getLoginUserById(Long userId) {
if(userId == null) return null;
Optional<User> optionalUser = userRepository.findById(userId);
if(optionalUser.isEmpty()) return null;
return optionalUser.get();
}
/**
* loginId(String)를 입력받아 User을 return 해주는 기능
* 인증, 인가 시 사용
* loginId가 null이거나(로그인 X) userId로 찾아온 User가 없으면 null
* loginId로 찾아온 User가 존재하면 User return
*/
public User getLoginUserByLoginId(String loginId) {
if(loginId == null) return null;
Optional<User> optionalUser = userRepository.findByLoginId(loginId);
if(optionalUser.isEmpty()) return null;
return optionalUser.get();
}
}
서비스 이다. PasswordEncoder는 나중에 시큐리티를 이용할때 다시 설명하고 구현하는 것으로 하고,
문서화를 연습하기 위한 서비스 임으로 각 서비스 마다 로직에 대해 주석을 달아 두었다.
-4. __[UserRepository.java]
public interface UserRepository extends JpaRepository<User, Long> {
boolean existsByLoginId(String loginId);
boolean existsByNickname(String nickname);
Optional<User> findByLoginId(String loginId);
}
Service를 구현하고 필요한 리포지토리들을 선언 하였다.
SpringBoot에서의 리포지토리는 나중에 장문의 글을 한번 쓸 예정이다.
728x90
'SpringBootProject' 카테고리의 다른 글
| Project Part1 - 로그인 구현하기. (5. Spring Security) (0) | 2024.11.28 |
|---|---|
| Project Part1 - 로그인 구현하기. (4. 세션을 활용한 로그인 기법) (0) | 2024.11.26 |
| Project Part1 - 로그인 구현하기. (3. 쿠키를 활용한 로그인 기법) (0) | 2024.11.25 |
| Project Part1 - 로그인 구현하기. (2. 로그인 컨트롤러, 로그인 기법) (0) | 2024.11.25 |
| SpringBoot Project 시작 (0) | 2024.11.24 |