๋๋ฌด ๊ธธ์ด์ ธ์ ์ค๋ช ๋ฐ๋ก ๋ถ๋ฆฌํ๋ค.
@Service
@RequiredArgsConstructor
@Slf4j
public class AuthService {
private final AuthenticateHandler authenticateHandler;
private final JwtProvider jwtProvider;
private final UserDetailsServiceImpl userDetailsServiceImpl;
public TokenDTO login(LoginRequestDTO request) {
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
/*
* (1) ์ฌ์ฉ์ ๊ฒ์ฆ(UserDetailsService)
* */
UserDetailsDTO userDetails = (UserDetailsDTO) userDetailsServiceImpl.loadUserByUsername(request.getUserId());
/*
* (2) ๋น๋ฐ๋ฒํธ ๋น๊ต(PasswordEncoder)
* */
if (!passwordEncoder.matches(request.getPassword(), userDetails.getPassword())) {
throw new BadCredentialsException("๋น๋ฐ๋ฒํธ๊ฐ ์ผ์นํ์ง ์์!(T_T)");
}
/*
* (3) Authentication ๊ฐ์ฒด ์์ฑ
* */
Authentication authentication = authenticateHandler.authenticate(request);
// (4) JWT ๋ฐ๊ธ
String accessToken = jwtProvider.createAccessToken(authentication);
// (5) ํด๋ผ์ด์ธํธ์ ํ ํฐ ์ ๋ฌ
return new TokenDTO(accessToken);
}
}
์ฌ๊ธฐ์ ์ฒซ๋ฒ์งธ ์์ ์ค๋ช !
UserDetailsDTO userDetails = (UserDetailsDTO) userDetailsServiceImpl.loadUserByUsername(request.getUserId());
(1) UserDetailsDTO
DB์์ ๊บผ๋ธ ๋ฐ์ดํฐ๋ ์ผ๋ฐ์ ์ผ๋ก ๋ ๊ฒ ๊ทธ๋๋ก์ ์ ๋ณด์ด๋ค.
๊ทธ๋์ USER ํ ์ด๋ธ = ํ์ค ์ธ๊ณ์ ์ฌ์ฉ์ ๋ฐ์ดํฐ์ด๋ค.
๋ฐ๋ฉด์
UserDetails = ์คํ๋ง ์ํ๋ฆฌํฐ๊ฐ ์ดํดํ๋ '์ธ์ฆ์ฉ ์ธ๊ฐ'์ด๋ค.
- DB → UserDTO (๊ทธ๋ฅ ๋ฐ์ดํฐ)
- UserDTO → UserDetailsDTO (์ธ์ฆ์ฉ ๊ฐ์ฒด)
๊ทธ๋ฆฌ๊ณ UserDetailsDTO๋ UserDetails ์ธํฐํ์ด์ค๋ฅผ ๊ฐ์ ธ์์ ๊ตฌํํ๊ธฐ ๋๋ฌธ์ ์ด์ฐจํผ UserDTO์ ํฉ์ณ์ ์ฌ์ฉํ์ง ๋ชปํ๋ค.
@Getter
@Setter
@ToString
public class UserDetailsDTO implements UserDetails {
private String userCd;
private String userId;
private String userNm;
private String password;
// ์์ผ๋ ์์ผ๋
private String bgColor;
private String textColor;
private String insertDatetime;
/*
* 1. Lombok ์ด๋
ธํ
์ด์
์ ์ฌ์ฉํด๋ UserDetails์์ ์๊ตฌํ๋ ์ธํฐํ์ด์ค๋ ์ง์ ์์ฑํด์ผ ํ๋ค.
* */
private final String role = "ROLE_USER";
@Override
public String getUsername() {
return userId;
}
@Override
public String getPassword() {
return password;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// ๊ณ ์ ๋ฐํ
return List.of(new SimpleGrantedAuthority(this.role));
}
/**
* ํ์์ ์ธ ์์๋ค : true๋ก ๊ณ ์
* */
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
1๏ธโฃ public class UserDetailsDTO implements UserDetails
UserDetails๋ Spring Security๊ฐ ์ฌ์ฉ์ ์ ๋ณด๋ฅผ ๋ค๋ฃจ๊ธฐ ์ํด ๊ฐ์ ๋ก ์๊ตฌํ๋ ์ธํฐํ์ด์ค์ด๋ค.
์ด ์ธํฐํ์ด์ค๋ฅผ ๊ตฌํํ๋ค๋๊ฑด ๐ “์ด ๊ฐ์ฒด๋ ๋ก๊ทธ์ธํ ์ฌ์ฉ์๋ก ์จ๋ ๋๋ค”๋ ๋ป.
๊ธฐ์กด์ UserDTO์ ๋ถ๋ฆฌํ ์ด์ .
2๏ธโฃ ํ๋๋ถ ์์ฑ
๋ณด์์ ์ง์ ๊ด์ฌํ๋๊ฑด ์๋์ง๋ง ๊ฐ ํ๋ก์ ํธ๋ณ๋ก ํ์ํ User์ ์ ๋ณด๋ฅผ ์์ฑํ๋ค.
3๏ธโฃ ๊ถํ(role) ์ค๊ณ - private final String role = "ROLE_USER"
Spring Security๋ ๊ถํ ์ด๋ฆ์ด ๋ฐ๋์ ROLE_๋ก ์์ํด์ผ ํ๋ค.
- USER โ
- ROLE_USER โญ๏ธ
์ง๊ธ ๋ด ํ๋ก์ ํธ์ ๊ฒฝ์ฐ ๊ถํ์ ๋ฑํ ์์ด์... ๊ทธ๋ฅ ROLE_USER๋ก ๋ฐ์๋๋ค. ๋์ค์ ํ์ํ๋ฉด ๋ฐ๋์๋...? ์์ง์ ๊ณํx
4๏ธโฃ ํต์ฌ ๋ฉ์๋
- getUsername() : Spring Security๊ฐ "์ด ์ฌ์ฉ์์ ์์ด๋๊ฐ ๋ฌด์์ธ๊ฐ" ๋ผ๊ณ ๋ฌผ์ ๋ ํธ์ถ๋๋ค. ๋ณดํต userId๋ฅผ ๋ฐํํ๋๊ฒ ์ ์(์ ๋ต์ ์๋~)
- getPassword() : DB์์ ์ ์ฅ๋ ์ํธํ๋ ๋น๋ฐ๋ฒํธ๋ฅผ ๊ฐ์ ธ์จ๋ค. ์ธ์ฆ ๊ณผ์ ์์ PasswordEncoder.matches()์ ์ฌ์ฉ๋จ
- getAuthorities() : "์ด ์ฌ์ฉ์์ ๊ถํ์ ์ ์"
- GrantedAuthority : ๊ถํ ์ธํฐํ์ด์ค
- SimpleGrantedAuthority : ๊ฐ์ฅ ๊ธฐ๋ณธ์ ๊ตฌํ์ฒด
5๏ธโฃ ๊ณ์ ์ํ ๊ด๋ จ ๋ฉ์๋
๋ณดํต ํ์์ ์ธ ์์๋ค์ด๊ธฐ ๋๋ฌธ์ true๋ก ๊ณ ์ ํ๋ค.
- public boolean isAccountNonExpired() { return true; } : ๊ณ์ ๋ง๋ฃ ์ฌ๋ถ
- public boolean isAccountNonLocked() { return true; } : ๊ณ์ ์ ๊น ์ฌ๋ถ. ๋ก๊ทธ์ธ ์คํจ 5ํ ์ ์ฑ ๋ง๋ค ๋ ์ฌ์ฉ
- public boolean isCredentialsNonExpired() { return true; } : ๋น๋ฐ๋ฒํธ ๋ง๋ฃ ์ฌ๋ถ. 90์ผ๋ง๋ค ๋ณ๊ฒฝ ์ ์ฑ ๋ ์ฌ์ฉ
- public boolean isEnabled() { return true; } : ๊ณ์ ํ์ฑํ ์ฌ๋ถ. ํํด, ํด๋ฉด ๊ณ์ ์ฒ๋ฆฌํ ๋ ์ฌ์ฉ
์ ๋ฐ๊ฑฐ ๋ง๋ค๋ฉด ์ข๊ธด ํ๋ฐ.. ๋ด ํ๋ก์ ํธ๋ ์ ์ฝ์ด ์๋ ํ๋ฆฌ(?)ํ ์คํ์ผ์ ์ถ๊ตฌํ๊ธฐ ๋๋ฌธ์ ๋ฐ๋ก ๊ตฌํํ์ง ์์๋ค.
๋๊ฐ ์ฅ๋์งํ๋ฉด ๋์ค์๋ผ๋ ๊ฐ๋ฐ์ ํ ๋ ค๋..?
(2) UserDetailsServiceImpl
์งํผํฐ์ ๊ณ ๊ฒฌ์ผ๋ก๋ ์ด ๋ถ๋ถ์ด Spring Security ์ธ์ฆ์ "์ฌ์ฅ"๋ถ๋ถ์ด๋ผ ํ์ ๋ค.
์ด๋ ๊ฒ๊น์ง ๊ฑฐ์ฐฝํ ํํ์ ์ธ์ค์ด์ผ...
@Service
@RequiredArgsConstructor
@Slf4j
public class UserDetailsServiceImpl implements UserDetailsService {
public final JdbcTemplate jdbcTemplate;
@Override
public UserDetails loadUserByUsername(String userId) {
String sql = "SELECT * FROM \"USER\" WHERE \"USER_ID\" = ?"; // ์ฌ๊ธฐ ์๋ฌ๋ ๋ฌด์ ๊ฐ๋ฅ
UserDetailsDTO userImpl = new UserDetailsDTO();
try {
UserDTO user = jdbcTemplate.queryForObject(
sql,
new Object[]{userId},
(rs, rowNum) -> { // Mapper
UserDTO u = new UserDTO();
u.setUserCd(rs.getString("USER_CD"));
u.setUserId(rs.getString("USER_ID"));
u.setUserNm(rs.getString("USER_NM"));
u.setPassword(rs.getString("PASSWORD"));
u.setBgColor(rs.getString("BG_COLOR"));
u.setTextColor(rs.getString("TEXT_COLOR"));
u.setInsertDatetime(rs.getString("INSERT_DATETIME"));
return u;
}
);
if (user == null) {
throw new UsernameNotFoundException("์ฌ์ฉ์ ์ ๋ณด ์์: " + userId);
}
userImpl.setUserCd(user.getUserCd());
userImpl.setUserId(user.getUserId());
userImpl.setUserNm(user.getUserNm());
userImpl.setPassword(user.getPassword());
userImpl.setBgColor(user.getBgColor());
userImpl.setTextColor(user.getTextColor());
userImpl.setInsertDatetime(user.getInsertDatetime());
return userImpl;
} catch (Exception e) {
e.printStackTrace();
throw new UsernameNotFoundException("์ฌ์ฉ์ ์ ๋ณด ์์: " + userId);
}
}
1๏ธโฃ public class UserDetailsServiceImpl implements UserDetailsService
UserDetailsService ๋ํ Spring Security๊ฐ ๋ก๊ทธ์ธ ์ ๋ฌด์กฐ๊ฑด ํธ์ถํ๋ ์๋น์ค์ด๋ค.
- ์ฌ์ฉ์๊ฐ ์์ด๋ + ๋น๋ฐ๋ฒํธ ์ ๋ ฅ
- Spring Security๊ฐ UserDetailsService๋ฅผ ์ฐพ์
- loadUserByUsername(username)์ ์๋์ผ๋ก ํธ์ถ
๋ฐ๋ผ์ ๊ฐ๋ฐ์๊ฐ ๋ฐ๋ก ํธ์ถํ์ง ์์๋ ์๋์ผ๋ก ์ฐ๊ฒฐ๋๋ค.
์ด๊ฑธ ์ง๊ธ implementํ ์ด์ ๋ ๊ทธ ์์ loadUserByUsername์ ์ค๋ฒ๋ผ์ด๋ํด์ ์ฌ์ฉํ ๋ ค๊ณ .
2๏ธโฃ JDBCTemplate ์ ํ ์ด์
JDBCTemplate๋ ๋๊ธฐ ์ฒ๋ฆฌ๊ฐ ๊ฐ๋ฅํ๊ณ , ์์ธ ํ๋ฆ์ด ๋ช ํํ๋ฉฐ, ํธ๋์ญ์ ๊ณผ ๋๋ฒ๊น ์ด ์ฝ๋ค. ์ด๊ฑฐ ์ฌ์ฉ ์ํ๋ฉด Supabase SDK๋ฅผ ์ฌ์ฉํ๋ ์๋ฐ์ ์๋๋ฐ, ๊ทธ๊ฑฐ ์ค์นํ๊ณ ์ธํ ํ๊ธฐ๊น์ง ๋ ์์ฒญ ์ค๋ ๊ฑธ๋ฆด๊ฒ์ด ๋น์ฐ์ง์ฌ, Kotlin ๋น๋๊ธฐ์ + SDK ์ ๋ฐ์ดํธ๋ ํ์ํ๊ธฐ ๋๋ฌธ์ ์ด๊ฒ์ ๊ฒ ๊ท์ฐฎ์ ์ด์๋ก JDBCTemplate๋ฅผ ์ ํํ๋ค...
3๏ธโฃ public UserDetails loadUserByUsername
์ ๋๋ก ์์์ ๋ฉ์๋๊ฐ ์๋๊ณ , UserDetailsService์ ๋ด์ฅ๋์ด ์๋ ๋ฉ์๋์ด๋ค.
Spring Security ๋ด๋ถ์์ AuthenticationManager → UserDetailsService.loadUserByUsername()์ ์คํํ๋ค.
๐ ๋ก๊ทธ์ธ ๋ฒํผ์ ๋๋ฅด๋ฉด ๋ฌด์กฐ๊ฑด ์คํ๋จ!
4๏ธโฃ SQL์์ฑ๋ฌธ
String sql = "SELECT * FROM \"USER\" WHERE \"USER_ID\" = ? ";
์ฐธ๊ณ ๋ก ํฐ๋ฐ์ดํ "USER"๋ก ์์ฑํด์ผ ํ๋ฏ๋ก(PostgreSQL์ ์ฌ์ฉํจ, ํ ์ด๋ธ๋ช & ์ปฌ๋ผ๋ช ์ ๋๋ฌธ์๋ก ํจ) ์ด์ค์ผ์ดํ ๋ฌธ์ \๋ฅผ "์์ ๋ฐ๋์ ์ฌ์ฉํด์ค๋ค.

์ด๋ฐ์์ผ๋ก ๋นจ๊ฐ์ ํ์์ ํจ๊ป ์๋ฌ ๋ฌธ๊ตฌ๊ฐ ๋จ๊ธด ํ๋๋ฐ, sql์ ์๋ ๋์๊ฐ๋ค. ์ด๊ฒ ์ซ์ผ๋ฉด DB์ค๊ณ ๋ ์๋ฌธ์๋ก ํ๋๊ฐใ
jdbcTemplate.queryForObject(sql, param[], RowMapper)
queryForObject: ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฟผ๋ฆฌ๋ฅผ ์คํํ์ฌ, ๋จ ํ๋์ ๊ฐ์ฒด๋ฅผ ์กฐํํ ๋ ์ฌ์ฉํ๋ ๋ฉ์๋. ๋จ๊ฑด ์กฐํ์ ์ต์ ํฉ. ๊ฒฐ๊ณผ๊ฐ 0ํ์ด๊ฑฐ๋ 2ํ ์ด์์ผ ๊ฒฝ์ฐ ์์ธ ๋ฐํ, ์ ์์ธ ๊ฒฝ์ฐ UserDTO ๊ฐ์ฒด ํ๋ ๋ฐํ
- ์ฒซ๋ฒ์งธ ์ธ์ : ์คํํ SQL. ?๋ ์์ง ๊ฐ์ด ์๋ ์๋ฆฌ
- ๋๋ฒ์งธ ์ธ์ : ํ๋ผ๋ฏธํฐ ๋ฐฐ์ด. SQL์ ์ฒซ๋ฒ์งธ ?์ ์ด ๋ฐฐ์ด์ ์ฒซ๋ฒ์งธ ๊ฐ์ธ userId๋ฅผ ๋ฃ์ด๋ฌ๋ผ๋ ๋ป
- ์ธ๋ฒ์งธ ์ธ์ : ๋๋ค์ RowMapper
(rs, rowNum) -> {...} : RowMapper์ ์ธํฐํ์ด์ค ๊ตฌํ. ์๋ ํํ๋
public UserDTO mapRow(ResultSet rs, int rowNum)
rs(ResultSet) : DB์์ ์กฐํํ ํ์ฌ ํ. ์ปค์๊ฐ ์ด๋ฏธ ํด๋น ํ์ ์์นํด ์์
rowNum : ๋ช๋ฒ์งธ ํ์ธ์ง(0๋ถํฐ ์์), query()์์ ์ฌ๋ฌ ํ ์ฒ๋ฆฌํ ๋ ์ ์ฉ
๋๋ค์ ๋ด๋ถ์์ UserDTO๋ฅผ ๋ฐํํ๋ ์์ ์ ํ ๊ฒ์ด๋ค.
(rs, rowNum) -> {
UserDTO u = new UserDTO();
u.setUserCd(rs.getString("USER_CD"));
u.setUserId(rs.getString("USER_ID"));
u.setUserNm(rs.getString("USER_NM"));
u.setPassword(rs.getString("PASSWORD"));
u.setBgColor(rs.getString("BG_COLOR"));
u.setTextColor(rs.getString("TEXT_COLOR"));
u.setInsertDatetime(rs.getString("INSERT_DATETIME"));
return u;
}
๊ทธ๋๊น ์ฌ์ค ์ด๊ฑด,
new RowMapper<UserDTO>() {
@Override
public UserDTO mapRow(ResultSet rs, int rowNum) throws SQLException {
UserDTO u = new UserDTO();
u.setUserCd(rs.getString("USER_CD"));
u.setUserId(rs.getString("USER_ID"));
u.setUserNm(rs.getString("USER_NM"));
u.setPassword(rs.getString("PASSWORD"));
u.setBgColor(rs.getString("BG_COLOR"));
u.setTextColor(rs.getString("TEXT_COLOR"));
u.setInsertDatetime(rs.getString("INSERT_DATETIME"));
return u;
}
}
์ด๊ฑฐ๋ ๋๊ฐ์ ๋ป์ด๋ค.
๋๋ค์ ๋ํ ์ค๋ช ์ ์ฌ๊ธฐ์...
5๏ธโฃ ๊ฐ์ฒด ๋ฐํ
SQL๋ก๋ถํฐ ๋ฐํ๋ ๊ฐ์ฒด ๊ฐ์ userDetailsDTO์ ๋ฃ์ด์ ์๋ ๋ฉ์๋ UserDetailsDTO userDetails๋ก ๋ฐํํ๋ค.

[์ฐธ๊ณ ์๋ฃ]
https://programmer93.tistory.com/68
Spring Security UserDetails, UserDetailsService ๋? - ์ฝ์ง์ค์ธ ๊ฐ๋ฐ์
Spring Security - UserDetails , UserDetailsService UserDetails ๋? Spring Security์์ ์ฌ์ฉ์์ ์ ๋ณด๋ฅผ ๋ด๋ ์ธํฐํ์ด์ค์ด๋ค. Spring Security์์ ์ฌ์ฉ์์ ์ ๋ณด๋ฅผ ๋ถ๋ฌ์ค๊ธฐ ์ํด์ ๊ตฌํํด์ผ ํ๋ ์ธํฐํ์ด์ค๋ก ๊ธฐ๋ณธ
programmer93.tistory.com
[Spring] Spring-Security / UserDetails์ UserDetailsService ์ปค์คํฐ๋ง์ด์ง
UserDetails์ UserDetailsService ๋ฅผ ์ด์ฉํ ์คํ๋ง ์ํ๋ฆฌํฐ ์ปค์คํฐ๋ง์ด์ง
velog.io
'Backend๐ฅ๏ธ > Javaโ(Spring๐)' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| [Spring] JWT ์ด์ฉํด์ ๋ก๊ทธ์ธ ๊ธฐ๋ฅ ๋ง๋ค๊ธฐ - ํํฐ ์์ฑ (0) | 2026.01.07 |
|---|---|
| [Spring] JWT ์ด์ฉํด์ ๋ก๊ทธ์ธ ๊ธฐ๋ฅ ๋ง๋ค๊ธฐ (0) | 2025.12.31 |
| [Spring] Spring Security ๋ง๋ณด๊ธฐ (0) | 2025.12.11 |
| [Spring] Spring Security - application.yml๋ก URL๊ถํ์ ๋์ ์ผ๋ก ๊ด๋ฆฌํ๊ธฐ (0) | 2025.10.20 |
| [Java]API ํธ์ถํด์ ๋ฌธ์ ๋ณด๋ด๊ธฐ๐จ (4) | 2025.06.12 |