트러블슈팅: 여행 일정 이미지가 업데이트되지 않는 문제 개선하기
들어가며
최근, 혼자옵서예 프로젝트의 백엔드 서버를 재배포하면서 서비스를 유지보수하고 있다.
이번 글에는 프로젝트를 점검하며 발견한 여행 일정 이미지가 업데이트 되지 않는 문제를 개선한 과정을 기록하고자 한다.
문제 인식
업데이트 되지 않는 여행 일정 이미지
여행 일정 페이지 테스트에서 에러가 발생했다.
일정 이미지를 업데이트 하면, 다음과 같이 미리보기가 업로드 되지 않고 이미지 업데이트에도 실패했다.
원인 파악
콘솔 확인
원인을 파악하고자 먼저 콘솔창을 열어, 에러 메세지가 뜨는지 확인했다.
변경할 이미지를 업로드하고 수정 확인 버튼을 클릭하면 콘솔창에 Request failed with status code 500 에러가 출력되었다.
이를 해석하면, 서버에 이미지 업데이트를 요청했을 때 상태코드 500의 Axios 에러가 발생함을 의미한다. 즉, 서버가 이미지 업데이트 요청을 받아 응답하는 동작이 정상 작동하지 않은 것이다.
서버 로그 확인
서버 문제임을 파악한 후, 통신 오류에 대한 서버 측 기록은 없는지 실행 로그를 확인했다. 로그에는 다음과 같은 기록이 남아있었다.
이 로그는 일정 페이지에 접속한 시점부터 이미지 업데이트에 실패하기까지의 실행 로그이다.
첫 번째 로그는 일정 페이지에 처음 접속했을 때, 일정 데이터를 받아 온 기록이다.
두 번째 로그는 서버로 전송된 이미지 데이터의 정보이다. 이를 토대로 서버가 이미지 데이터를 받는 단계까지는 성공한 사실을 파악했다.
세 번째 로그는 에러 로그이다. 이미지 저장 경로를 찾을 수 없다는 내용을 담고 있다.
마지막 로그는 클라이언트의 이미지 업로드 요청에 대한 기록이다. 서버 오류를 뜻하는 상태코드 500이 눈에 띄었다.
실행 로그를 확인하여 에러의 원인이 일정 이미지 저장 경로임을 유추할 수 있었다.
서버가 시스템이 지정한 경로 ‘public/img’에 이미지를 저장하려 했으나, 경로를 찾을 수 없어 에러가 발생한 것이다.
문제 해결
코드 확인
일정 이미지 업데이트 시스템
문제를 해결하기에 앞서, 혼자옵서예의 일정 이미지는 어떤 과정을 거쳐 업데이트되는지 코드로 파악했다.
일정 이미지 업데이트 시스템은 다음과 같이 추상화할 수 있었다.
- 일정 이미지 업데이트 시스템
- 사용자가 일정 이미지를 업로드한다.
- 클라이언트에서 FormData 객체를 생성하고 업로드된 이미지 파일을 프로퍼티로 추가한다.
- 클라이언트가 FormData 객체를 서버에 POST한다.
- 서버는 일정 이미지를 업데이트하고 이미지 URL을 생성하여 클라이언트에 응답한다.
- 클라이언트는 이미지 URL을 이용해 이미지 미리보기를 생성한다.
코드에서 개선안 찾기
일정 이미지가 어떻게 업데이트 되는지 이해한 후, 코드를 구체적으로 분석했다. NestJS에 대한 지식이 전무하기 때문에 공식문서와 검색을 적극 활용했다.
이미지 업로드에는 Multer가 사용되었다. Multer은 Node.js 기반 프로젝트에서 범용적인 모듈이다. 주로 파일을 업로드하는데 사용된다.
공식문서에 따르면 Multer은 업로드된 파일을 저장할 수 있는, 스토리지 엔진 diskStorage를 제공한다. 혼자옵서예 백엔드 코드도 이 엔진을 사용하였는데, 옵션으로 파일 저장 경로를 지정할 수 있다.
예상대로 파일 저장 경로는 public/img로 설정되어 있었다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
MulterModule.register({
storage: diskStorage({
destination(req, file, callback) {
try {
Logger.log(file);
callback(null, "public/img"); // 업로드 파일이 저장되는 경로
} catch (error) {
callback(error, null);
Logger.error(error);
}
},
// ...
}),
});
그러나 배포된 서버에는 public/img 디렉터리가 없다. git으로 관리하지 않기 때문에 서비스와 연결된 레포지토리에 존재하지 않는다.
그 이유는 일정 이미지 데이터의 특징과 관련 있다. 데이터는 사용자가 이미지를 업로드할 때 동적으로 생성된다. 또한 사용자가 이미지 삭제나 수정을 요청하면 변경된다. 이는 소스 코드의 버전 관리를 목적으로 하는 git의 용도와 맞지 않다.
그러나 클라이언트에 응답으로 보낼 이미지 URL을 생성하려면, 파일을 저장할 디렉터리가 필요했다.
이에 따라 git 레포지토리에 등록할 필요 없이, 서버에 디렉터리를 생성할 수 있는 방법을 조사했다.
그 결과, Multer로 파일 업로드 로직을 구현한 사례에서 힌트를 얻을 수 있었다. Node.js 파일시스템(fs)을 이용하여 디렉터리를 생성하는 방법이다.
파일시스템 메서드 existsSync, mkdirSync를 사용하면 타겟 디렉터리가 존재하는지 확인하고, 필요시 동적으로 생성할 수 있다.
조사한 내용을 토대로 다음과 같이 코드를 수정했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
MulterModule.register({
storage: diskStorage({
destination(req, file, callback) {
try {
Logger.log(file);
//수정된 코드
const imagePath = path.join(__dirname, "..", "..", "public", "img");
if (!existsSync(imagePath)) {
mkdirSync(imagePath);
}
callback(null, imagePath);
} catch (error) {
callback(error, null);
Logger.error(error);
}
},
// ...
}),
});
추가 문제 해결
에러 로그 해결하기
1차로 코드를 수정하였으나 문제를 해결할 수 없었다.
https://res.cloudinary.com/dr26wooar/image/upload/v1722868854/blog/honja-op-seo-yeah/serverLog2.png
위와 같이 여전히 경로를 찾을 수 없다는 에러가 발생했다. 디렉터리 생성에 실패한 기록은 덤이다.
다른 문제가 있는지 에러 로그를 토대로 원인을 다시 조사했다. 흔한 에러인지, 도움을 구하는 글을 여러 개 찾을 수 있었다. 그 중 하나의 도움을 받아 해결을 시도했다.(클릭 시 이동)
글을 토대로 추측한 원인은 다름 아닌 ‘nested directory’였다. 새로 생성할 디렉터리는 public, img 2번의 깊이로 중첩되어 있었다. 즉, public 디렉터리를 생성하고 그 내부에 다시 img 디렉터리가 생성되어야 했다.
나는 1차로 수정한 코드만으로 이 동작이 수행될 것이라 생각했다. 그러나 mkdirSync는 기본적으로 중첩 디렉터리를 한 번에 생성할 수 없었다.
이에 따라 nested directory를 생성하기 위해서는 별도의 추가 설정이 필요했다. 바로 ‘recursive’ 라는 옵션이다. 공식문서에 따르면 상위 디렉터리를 반환하여 하위 디렉터리가 생성될 경로를 찾을 수 있게 된다.
여담으로 recursive 옵션은 이러한 mkdirSync의 문제점을 인지하고 Node js 10.12.0 버전 이후에 추가된 기능이라고 한다. 혹여, 문제가 해결되지 않는다면 사용하고 있는 Node js의 버전을 확인하자.
1
2
3
4
5
6
7
8
// ...
const imagePath = path.join(__dirname, "..", "..", "public", "img");
if (!existsSync(imagePath)) {
mkdirSync(imagePath, { recursive: true });
}
// ...
recursive: true를 설정한 끝에, 드디어 에러 없이 일정 이미지를 업데이트 하는데 성공했다.



