@ResponseStatus 204 No Content
tl;dr
SpringBoot controller에서 결과값을 반환하지 않을 때는
- @ResponseStatus(value = HttpStatus.NO_CONTENT)를 함수 위에 추가해준다. -> status 204 반환 https://stackoverflow.com/questions/32396884/return-http-204-on-null-with-spring-restcontroller
옛날에 만든 프로젝트를 스프링부트로 옮기는 와중에 이전 코드에 존재하는 동작중
parameter로 값을 넘겨주면 해당 값을 DB 조회해 받아온 값으로 엑셀로 만들어 다운로드할 수 있게
만들어 둔 게 있었다.
문제는 다운로드 파일을 서버에 한번 저장한 다음 해당 파일 경로를 바로 접근한다는 게 오늘의 오류의 발생 지점이었다.
- controller는 파라미터를 받아 조회 후 엑셀을 생성, 서버 내 폴더 저장
- 해당 작업이 완료되면 서버에 요청해 해당 파일을 다운로드하게 구성
다른 controller와 달리 이건 반환 값이 없다! ajax 요청 후, 반환값이 없으니 계속 오류가 뜨는 거다.
해당 오류를 재현하게 단순한 프로젝트를 구성해 보자.
- 페이지에서 parameter( int a, b)로 값을 전달 (ajax 이용)
- 전달한 값을 더해 파일(txt)에 적은 뒤, 서버에 저장 (spring boot folder 내 저장)
- 해당 파일을 다운로드
오류 재현을 위해
spring boot initializer를 이용해 프로젝트를 구성
사진에 보는 것과 구성한 뒤, 프로젝트 빌드
프로젝트의 폴더 구조는 아래와 같다.
이제 html, js, controller를 구성해 준다.
1. 먼저 home.html
<html>
<head>
<title>test</title>
<script src="https://code.jquery.com/jquery-3.6.3.min.js" integrity="sha256-pvPw+upLPUjgMXY0G+8O0xUf+/Im1MZjXxxgOcBQBXU=" crossorigin="anonymous"></script>
</head>
<body>
test page!
<br>
a : <input type="text" id="input_a"/>
<br>
b : <input type="text" id="input_b"/>
<input type="button" id="button" value="download"/>
<a id='download' href="" download = "" style="display: none;"/>
</body>
<script type="text/javascript" src="./home.js"></script>
</html>
2. 이제 home.js
$(document).ready(function() {
console.log("home!")
addAndSave();
});
// download 버튼 누를때 동작하게끔 구성
function addAndSave(){
$("#button").on("click", function() {
let a = $("#input_a").val();
let b = $("#input_b").val();
console.log(a,b);
let filename = "test.txt";
$.ajax({
url : "addAndSave",
method : "GET", // get 동작으로 서버에 요청
data : {
a : a,
b : b,
filename : filename
},
}).done(function() {
$("#download").attr("href","file/"+filename);
$("#download").attr("download", filename);
document.getElementById("download").click();
});
});
}
3. controller의 GetMapping
@Autowired
ResourceLoader resourceLoader;
@GetMapping("/addAndSave")
public void addAndSave(
@RequestParam("a") int a,
@RequestParam("b") int b,
@RequestParam("filename") String filename
) throws IOException {
String folderPath = resourceLoader.getResource("classpath:static/file").getURI().getPath();
File file = new File(folderPath, filename);
String text = "a = "+a+", b = "+ b + ", a+b = "+(a+b);
FileWriter fw = new FileWriter(file);
fw.write(text);
fw.close();
}
이제 html에 숫자를 입력한 뒤, download 버튼을 누르면
target 폴더 내 test.txt 가 생성되어 파일이 존재하는 걸 확인할 수 있다.
문제는 반환값이 존재하지 않기 때문에 웹페이지 상에서는 404 에러가 뜬다.
찾아보니 SpringBoot controller에서 결괏값을 반환하지 않을 때는
https://stackoverflow.com/questions/32396884/return-http-204-on-null-with-spring-restcontroller
함수 위에 해당 요청은 반환값이 없다는 걸 알려줘야 한다.
@GetMapping("/addAndSave")
@ResponseStatus(value= HttpStatus.NO_CONTENT)
public void addAndSave(
// blar blar~~
위 annotation 추가 후 동작하면
해당 GET 요청 시 204 statusCode를 반환하고 이전처럼 에러가 나지 않는다.
물론 파일 다운로드도 정상적으로 되는 걸 확인할 수 있다. (테스트 때문에 동일 파일이름이 존재해서 (2))
그럼 204 NO Content 응답에 대해서 좀 더 확인해 보자.
https://ko.wikipedia.org/wiki/HTTP_%EC%83%81%ED%83%9C_%EC%BD%94%EB%93%9C
보통은 DELETE 나 PUT 요청에 사용된다고 하는데…
나처럼 무언가 반환할 값이 없을 때도 사용하면 될듯하다 (꼭 DELETE 요청이 아니더라도…)