๐จ Could not write JSON: getOutputStream() has already been called for this response
๐ฅ ์ค๋ฅ ์์ธ
HttpServletResponse์ getOutputStream() ๋๋ getWriter()๊ฐ ์ฌ๋ฌ ๋ฒ ํธ์ถ๋์์ ๋ ๋ฐ์ํ๋ ๋ฌธ์ !
์ฆ, ์๋ต์ ํ ๋ฒ ๋ณด๋ธ ํ์ ๋ ์๋ต์ ๋ณด๋ด๋ ค๊ณ ํ ๋ ๋ฐ์ํ๋ค.
๐ ์๋ฌ ์ฝ๋ (์์ ์ )
โก๏ธ ์ฟ ํค๋ฅผ response.addCookie()๋ก ์ค์ ํ ํ, ResponseEntity๋ก JSON ์๋ต์ ๋ฐํ.
โก๏ธ ํ์ง๋ง ResponseEntity๊ฐ HTTP ์๋ต์ ์ค์ ํ๋ ๊ณผ์ ์์ response.getOutputStream()์ ๋ค์ ํธ์ถํ๊ธฐ ๋๋ฌธ์ ์ถฉ๋์ด ๋ฐ์.
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody AuthRequest authRequest, HttpServletResponse response) {
try {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword())
);
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
String accessToken = jwtUtil.generateAccessToken(userDetails);
String refreshToken = jwtUtil.generateRefreshToken(userDetails);
// โ HttpOnly ์ฟ ํค๋ก refreshToken ์ค์
Cookie refreshTokenCookie = new Cookie("refreshToken", refreshToken);
refreshTokenCookie.setHttpOnly(true);
refreshTokenCookie.setSecure(true);
refreshTokenCookie.setPath("/");
refreshTokenCookie.setMaxAge(7 * 24 * 60 * 60);
response.addCookie(refreshTokenCookie);
Map<String, Object> responseBody = new HashMap<>();
responseBody.put("success", true);
responseBody.put("message", "๋ก๊ทธ์ธ ์ฑ๊ณต!");
responseBody.put("tokens", Map.of("accessToken", accessToken)); // "refreshToken", refreshToken
return ResponseEntity.ok(response);
} catch (AuthenticationException e) { // ๋ก๊ทธ์ธ ์คํจ
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(Map.of("success", false, "message", "์์ด๋ ๋๋ ๋น๋ฐ๋ฒํธ๊ฐ ์ฌ๋ฐ๋ฅด์ง ์์ต๋๋ค."));
}
}
๐ ํด๊ฒฐ ๋ฐฉ๋ฒ
๐ ์์ ์ฝ๋
โก๏ธ ResponseEntity์์ Set-Cookie ํค๋ ์ง์ ์ค์
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody AuthRequest authRequest) {
try {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword())
);
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
String accessToken = jwtUtil.generateAccessToken(userDetails);
String refreshToken = jwtUtil.generateRefreshToken(userDetails);
// โ
Set-Cookie ํค๋ ์ถ๊ฐ
String cookieHeader = "refreshToken=" + refreshToken + "; Path=/; HttpOnly; Secure; Max-Age=" + (7 * 24 * 60 * 60);
HttpHeaders headers = new HttpHeaders();
headers.add("Set-Cookie", cookieHeader);
return ResponseEntity.ok()
.headers(headers)
.body(Map.of(
"success", true,
"message", "๋ก๊ทธ์ธ ์ฑ๊ณต!",
"accessToken", accessToken
));
} catch (AuthenticationException e) { // ๋ก๊ทธ์ธ ์คํจ
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(Map.of("success", false, "message", "์์ด๋ ๋๋ ๋น๋ฐ๋ฒํธ๊ฐ ์ฌ๋ฐ๋ฅด์ง ์์ต๋๋ค."));
}
}
'Backend > spring' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
ํ๊ฒฝ ๋ณ์ ๊ด๋ฆฌ (@Value, @ConfigurationProperties) (0) | 2025.02.26 |
---|---|
[error] java.lang.IllegalArgumentException: Name for argument of type [int] not specified (0) | 2025.02.23 |
[JAVA] ์ฟ ํค(Cookie) ์ค์ ๋ฐฉ๋ฒ (0) | 2025.02.23 |
[Spring Security] CSRF & CORS ๊ฐ๋ ๋ฐ ์ค์ ๋ฐฉ๋ฒ (0) | 2025.02.23 |
[error] conversionServicePostProcessor Bean ์ค๋ณต ์ค๋ฅ (0) | 2025.02.23 |