housesService와 해당 Service가 있는 모듈이 import된 모든 부분에서 위와 같은 에러가 발생했다. multer-s3에 있는 함수에서 bucket값이 required값이기 때문에 에러가 발생함은 인지하고 있었다. 하지만 어떻게 mocking을 진행해야 하는지 도통 알 수 없었다.
나는 일단은 문제를 해결할 방법을 먼저 고민하게 되었다. 해당 문제를 해결하지 못하면 housesModule에 관련된 모든 파일들을 테스팅할 수 없기 때문에 일단 문제를 해결해 보려고 했다.
주어진 문제는 bucket값이 반드시 필요하다는 것이다. 문제는 bucket이 env에 들어가 있는 환경변수라는 점이다. test환경에서는 일반 env를 읽지 않는다 따라서 해당에러가 발생했음을 쉽게 유추할 수 있다.
S3의 버켓이름을 넣어줘도 될까?라는 의문이 가장 많이 들었다. 그냥 넣어주면 쉽게 끝나겠지만, 우리는 DB도 mocking해서 가짜 객체를 만들어서 테스트를 검증하는데, S3라고 다를까? 그래서 일단 테스트 진행시 S3를 사용하지 않는 쪽으로 변경시켜 보았다.
export const multerOptions = multer({
storage: multerS3({
s3,
bucket,
acl: 'public-read',
key(req, file, callback) {
callback(null, `nest-project/${Date.now() + file.originalname}`);
},
});
fileFilter: fileFilter,
limits: { fileSize: 10 * 1024 * 1024, files: 10 },
});
기존의 위와 같은 코드를 아래와 같이 변경시켰더니 실질적으로 bucket is required 에러는 사라졌다.
const storage = () => {
if (process.env.NODE_ENV === 'test') {
return multer.memoryStorage();
}
return multerS3({
s3,
bucket,
acl: 'public-read',
key(req, file, callback) {
callback(null, `nest-project/${Date.now() + file.originalname}`);
},
});
};
export const multerOptions = multer({
storage: storage(),
fileFilter: fileFilter,
limits: { fileSize: 10 * 1024 * 1024, files: 10 },
});
분명 bucket is required 에러는 사라졌지만 자꾸만 의구심이 들었다. S3에 저장하는 코드를 메모리스토리지 저장으로 테스트 하게 된다면 multer upload에 대한 의존성을 가진 기능은 동작하겠지만, S3에 저장이 되는건지 안되는건지.. S3에서 만약에 문제가 발생하면 어떻게 확인할 수 있을까?
그래서 S3를 mocking할 방법을 찾아서 구글을 뒤졌다.
구글을 통해서 S3를 mocking할 방법을 찾아보니 'aws-sdk-mock'이라는 라이브러리가 존재하는 것과 사용하는 예시 코드를 볼 수 있었다.
하지만 내 코드는 S3에 이미지를 업로드 하는 직접적인 API같은게 존재하지 않고, @UploadFile 데코레이터를 통해 이미지를 업로드 시키며, 해당 데코레이터의 3번째 인자인 Express.MulterOption타입의 객체에서 사용하게 되어 위의 방법을 적용시키기 어려웠다.
그러다가 생각해보니 왜 ? 저런 에러가 발생했던 거지? 라는 의문이 들었다. 물론 bucket이 환경변수에서 읽을 수 없기 때문이라는 단편적인 이유는 알고 있지만, controller의 @decorator에서 사용되는 건데 왜 모든 houses.Service를 임모트하는 모듈에서는 다 에러가 발생하는건가? 라는 의문이었다.
나는 멀터와 S3에 관련된 모든 내용을 따로 config, Service등으로 나누지 않고 하나의 파일에 몰아넣었었다.
그래서 그런건지 houses.Service에 존재하는 S3에서 이미지를 지우는 함수가 존재하는데, 해당 함수를 불러올때 파일 자체를 읽어보다 보니 갑자기 타입에러가 컴파일 중에 감지 되는 것이었다.
해당 함수에 관련된 내용을 모두 주석해보니 에러는 발생하지 않았다. 하지만 그렇다고 지울 수 없는 노릇이니 내가 생각한 방법은 두 가지였다.
이미지를 지우는 함수를 따로 fileModule로 빼서 controller와 Service를 작성하고 housesMoudule로 임포트 시켜서 사용하는 방법
그냥 S3를 어떻게든 Mocking해서 Bucket을 채우는 방법
1번은 이후 추가로 또 어쨋든 S3를 모킹해서 또 다른 testcode를 작성해야한다. 어짜피 1번이든 2번이든 2번과정을 거쳐야 하기 때문에 시간이 부족한 나는 2번과정을 진행하기로 결정했다.
그렇다면 어떻게 Mocking해야할까? 이미 'aws-sdk-mock'이라는 라이브러리는 사용하기 힘들다는 것을 알았는데..
그렇게 또 구글링을 하다보니 'mock-aws-s3'라는 라이브러리를 찾을 수 있었다. 가장 눈에 띄었던것은 npm에서 해당라이브러리 깃허브에 들어가 보았을 때 가장 윗줄의 함수! createBucket()이었다.
지금 나는 그 Bucket이 읽히지 않아서 에러가 발생하는데 가짜 bucket을 만들 수 있다면, 쉽게 해결될 것이라 생각했다.
코드의 샘플을 따라서 코드를 작성했다.
const storage = () => {
if (process.env.NODE_ENV === 'test') {
AWSMock.config.basePath = './testS3';
const params = { Bucket: 'testBucket' };
const mockS3 = new AWSMock.S3({ params });
mockS3.createBucket(params, function (err) {
if (err) {
console.error(err);
}
});
bucket = params.Bucket;
return multerS3({
s3: mockS3
bucket,
acl: 'public-read',
key(req, file, callback) {
callback(null, `nest-project/${Date.now() + file.originalname}`);
},
});
}
이렇게 내 코드에 맞게끔 작성해 보니 multerS3()함수의 첫번째 인자인 s3의 타입이 S3Client더라.. 하지만 mockS3는 S3의 타입을 가졌었다. 에효.. 괜히 코드 좀 쉽게 짜보겠다고 S3안쓰고 multer-s3라이브러리를 사용했더니 이러한 에러가 발생했다. 따라서 mockS3에 대한 타입을 강제해 주었다.
s3: mockS3 as unknown as S3Client & AWSMock.S3,
이후에 다시 houses.Service를 테스트 해주니 에러는 발생하지 않았다. 와 해결 끝 짝짝짝👏👏👏!! 하고 싶었지만, 또 의문이 들었다. 과연 Bucket은 제대로 생성된게 맞을까? 내가 변수로 직접 써준 const params = { Bucket: 'testBucket' }
이 그냥 문자열로만 들어가서 의미가 없으면 어떻하지? 의문이 들었다.
그래서 createBucket()
으로 bucket을 확인해 보고자 했다.
처음에는 S3에 존재하는 .listBuckets()
함수를 이용해 보고자 했는데, 해당함수가 없다는 에러를 반환했다. 실제 aws-sdk의 S3에는 해당함수가 존재하겠지만, mock-aws-s3라이브러리에는 존재하지 않는 함수라서 사용할 수 없었다.
그렇다면 내가 어떻게해야 해당 버켓을 만들어 졌는지 확인할 수 있을까? './testS3'
라는 경로로 'testBucket'
을 생성하기 때문에 fs.existSync(Path)
를 이용해 해당 디렉토리에 해당 내용이 있는지 확인하는 방법을 선택했다.
mockS3.createBucket(params, function (err) {
if (err) {
console.error(err);
} else {
console.log('버켓 생성 성공');
const isBucket = fs.existsSync('./testS3/testBucket');
console.log(isBucket);
}
});
만약 해당 경로에 내용이 있다면 true를 반환해 줄 것이다. console을 찍어서 확인하니 정상 반환하는 것은 확인할 수 있었다.
또한 해당 디렉토리를 들어가 보았을 때 폴더가 생성된 것도 확인할 수 있었다. '아 이 라이브러리는 S3대신 폴더를 만들어서 대체해 주는구나' 라는 생각을 할 수 있었다.
그런데 이런 빈 폴더를 만들어 둬야하나? 의문이 들었지만, 생성자체는 잘 확인되었고, 에러도 발생하지 않았다. 하지만 이게 정답일것이라 생각이 전혀 들지 않는다. 이 S3에 와 multer 때문에 진짜 3~5일은 고생한 것 같은데 해결책이 마땅히 나오지 않는다.
누군가 이건 틀렸어. 이건 잘못됬어. 이게 잘 되었어. 이게 맞아. 판단해 줄 수 있는 사람이 있으면 좋겠는데 나 같은 취업준비중인 개발자는 어떻게 이게 맞는건지 틀린건지 확신을 가지고 만들 수 있는지 의문이다.