NestJS 로 블로그를 만들던 도중 백엔드에서 사용자 엔티티를 업데이트하기 위해 이미지파일 경로를 조립후 클라이언트에 전송해야 할 일이 생겼다.
파일 서비스에서 이를 처리하도록 구현했고 configService 에서 가져온 백엔드 도메인과 /static/images 를 조립해 정상적인 경로를 반환할것이라 생각했다.
@Injectable()
export class FilesService {
private readonly baseDir: string;
private readonly tempPath: string;
private readonly staticPath: string;
constructor(private readonly configService: ConfigService<AllConfigType>) {
this.baseDir = this.configService.get('app.workingDirectory', { infer: true });
this.tempPath = path.join(this.baseDir, 'public', 'temp');
this.staticPath = path.join(
this.configService.get('app.backendDomain', { infer: true }),
'static',
'images',
);
}
...
임시 경로에서 파일을 옮긴 후 생성된 staticPath 를 이용해 UsersService 에서 사용자 엔티티를 save 할 수 있을 것이다.
// files.service.ts
async uploadUserProfileImage(filename: string) {
const destination = path.join(this.baseDir, 'public', 'images', 'users');
if (!fs.existsSync(destination)) {
fs.mkdirSync(destination, { recursive: true });
}
const tempFilePath = path.join(this.tempPath, filename);
const filePath = path.join(destination, filename);
await fs.promises.rename(tempFilePath, filePath);
return path.join(
this.staticPath,
'users',
filename
);
}
서버를 키고 이미지가 적용되었는지 확인해보니 404 에러가 떴다. 500이 아닌 404 라면
둘중 하나일텐데
Failed to load resource: the server responded with a status of 404 (Not Found)
프론트단 axios 에서 formData 에 파일을 제대로 넣었기 때문에 아무리 봐도 후자인 듯 싶어서 url 을 확인해봤다.
실패한 요청을 보니 프론트 url 과 백엔드 url 이 같이 붙어있다.
https://ywoosang-blog-front.run.goorm.io/ywoosang-blog.run.goorm.io/static/images/users/6ef8af70-b1f6-42f4-8c08-69dbb4878f46.jpeg
분명히 백엔드도메인으로 조립해 보냈는데 요청이 저렇게 들어간게 좀 이상했다. 바로 이미지 태그를 확인했다.
<img data-v-54003fcc="" src="https:/ywoosang-blog.run.goorm.io/static/images/users/6ef8af70-b1f6-42f4-8c08-69dbb4878f46.jpeg" alt="User Avatar">
자세히 보다보니 https:/
에 /
가 한 개 있는걸 찾았다. 찾는데 좀 오래걸렸다.
백엔드 .env.dev 에서 백엔드도메인을 잘못 붙인줄 알고 확인해봤는데 이것도 제대로 들어가 있었다.
마지막으로 path.join 을 의심해봤는데, 문제는 이거였다.
stackoverflow 를 찾아봤고 결론은 'path.join 을 url 조립에 사용하지 않아야한다' 였다.
https://stackoverflow.com/questions/16301503/can-i-use-requirepath-join-to-safely-concatenate-urls
단순히 이미지 경로만 필요하므로, 아래처럼 protocol&domain name + extension 으로 나눠서 해결했다.
const extention = path.join(
this.staticPath,
'users',
filename
);
return `${this.backendDomain}/${extention}`;