1. 엔티티
User
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "user_tb")
@Getter
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id; // null도 처리하도록 Integer
@Column(unique = true, nullable = false)
private String username; // 사용자가 ID로 인식하는 필드
@Column(nullable = false)
private String password;
@Column(nullable = false)
private String email;
@CreationTimestamp
private Timestamp createdAt;
}
2. 리포지토리
UserRepository
@Repository
@RequiredArgsConstructor
public class UserRepository {
private final EntityManager em;
public User findByUsername(String username) {
// username이 db에 있는지 조회하는 쿼리문
Query q = em.createQuery("select u from User u where u.username = :username", User.class);
q.setParameter("username", username);
try {
return (User) q.getSingleResult();
} catch (RuntimeException e) {
throw new RuntimeException("아이디 혹은 패스워드가 일치하지 않습니다.");
}
}
}
2-1. 리포지토리 테스트
@Import(UserRepository.class)
@DataJpaTest
public class UserRepositoryTest {
@Autowired
private UserRepository userRepository;
@Test
public void findByUserName_test() {
// given
String username = "ssar"; // 성공
// String username = "hello"; // 실패
// when
User user = userRepository.findByUsername(username);
// then, import 는 assertj로 합니다.
Assertions.assertThat(user).isNotNull();
}
}
- 테스트는 성공과 실패 모두를 테스트해야 합니다.
- Assertions는 프로그래밍에서 코드의 특정 조건이 참인지 여부를 검증하기 위해 사용되는 도구입니다.
- 주로 디버깅 및 테스트 목적으로 사용합니다.
3. 서비스
UserService
@RequiredArgsConstructor
@Service
public class UserService {
private final UserRepository userRepository;
public User 로그인(UserRequest.LoginDTO loginDTO) {
User userPS = userRepository.findByUsername(loginDTO.getUsername());
// 패스워드 일치 확인
if (!userPS.getPassword().equals(loginDTO.getPassword())) {
throw new RuntimeException("아이디 혹은 패스워드가 일치하지 않습니다.");
// 개발자를 위한 로그 작성 생략
}
return userPS;
}
}
- 패스워드 불일치 횟수 등을 개발자가 알기 위한 정보를 개발자만 보이는 로그를 남겨야 합니다.
4. 컨트롤러
@RequiredArgsConstructor
@Controller
public class UserController {
private final UserService userService;
private final HttpSession session;
@GetMapping("/login-form")
public String loginForm() {
return "user/login-form"; // user 폴더 안에 있는 view
}
@PostMapping("/login")
public String login(UserRequest.LoginDTO loginDTO) {
User sessionUser = userService.로그인(loginDTO);
session.setAttribute("sessionUser", sessionUser);
return "redirect:/";
}
// 포스트맨으로 테스트를 하기 위한 메서드
@ResponseBody
@GetMapping("/test/auth")
public String testAuth() {
User sessionUser = (User) session.getAttribute("sessionUser");
if (sessionUser == null) {
return "인증안됨";
}
return "인증됨 : " + sessionUser.getUsername();
}
}
- 비밀번호는 쿼리 스트링에 노출되면 안되므로 post 방식 요청을 보냅니다.
- 비밀번호를 세션에 기록하면 안됩니다.
- username과 password가 db에 있는 정보와 일치하면 세션에 username을 기록합니다.
로그인 매핑을 /user/login이 아니라 /login으로 하는 이유
→ 로그인 과정은 필터를 거칠 필요가 없기 때문입니다.
Share article