마트료시카 객체 정제하기: 모든 필드를 명시적으로 써야 해요?
240825 TIL | 데이터소스가 사기인데 왜 레포 주입해서 쓰는거임?
마트료시카 객체 정제하기: 모든 필드를 명시적으로 써야 해요?
🪆 마트료시카 객체 정제하기: 모든 필드를 명시적으로 써야 해요?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
* 게시물의 목록을 조회합니다. content는 20자 까지만 조회됩니다.
* @param queries
* @returns 게시물 목록. 없는 경우 빈 배열
*/
async findAll(hashtag: string, queries: PostingQueryDto): Promise<PostingResponseDto[]> {
const rawPostings = await this.getRawPostings(hashtag, queries);
// hashtag depth 정리 & content 글자 수 제한 적용해 반환
return rawPostings.map((rawPosting) => {
const posting = this.getPostingObjWithHashtags(rawPosting);
posting.content = posting.content.substring(0, 20);
return posting;
});
}
// 조인으로 인한 hashtag의 depth를 정리한 새 객체를 반환합니다.
private getPostingObjWithHashtags(postingObj: Posting): PostingResponseDto {
const hashtags: string[] = postingObj.postingHashtags.map((postingHashtag) => postingHashtag.hashtag.name);
return {
id: postingObj.id,
contentId: postingObj.contentId,
type: postingObj.type,
title: postingObj.title,
content: postingObj.content,
writer: postingObj.writer,
hashtags,
viewCount: postingObj.viewCount,
likeCount: postingObj.likeCount,
shareCount: postingObj.shareCount,
createdAt: postingObj.createdAt,
updatedAt: postingObj.updatedAt,
};
}
코드 설명
findAll()
- 게시물 목록 조회 API로,
hashtag
의name
을 가져오기 위해getRawPostings()
를 통해posting
-posting_hashtag
-hashtag
2번의 조인을 한다. - 그래서
rawPostings
엔 이러한 형태의 데이터가 담겨서 온다.1 2 3 4 5 6 7 8 9 10 11 12 13 14
posting { postingHashtags: [ { hashtag: { name: '원하는 값', }, }, { hashtag: { name: '원하는 값', }, }, ] }
getPostingObjWithHashtags()
- 원하는 데이터 형태는 이렇기 때문에, 이를 위해 객체의 depth를 정리해주는 private 함수이다.
1 2 3
posting { hashtags: ['원하는 값', '원하는 값'], }
고민: return
값
- 한 필드(
hashtags
)만 바꾸면 되는데 저 많은 필드들을 다시 적어줘야 한다는 게 번거롭다고 느꼈다. - 함수의 return type도 정확히 명시하고 있어 굳이 저렇게 쓰지 않아도 괜찮을 것 같았다.
나름대로 생각해본 다른 방안
delete 연산자
postingHashtags
가 hashtags
로 바뀌어야 하니, 처음엔 단순히 이렇게 생각했다.
1
2
3
4
delete postingObj.postingHashtags;
postingObj['hashtags'] = hashtags;
return postingObj;
- 그러나 타입 안정성의 이유로 TS Error가 나
delete
를 쓰지 못했고, postingObj
는 이미Posting
타입의 변수이기 때문에 동적으로 프로퍼티를 추가하는 방법 또한 먹히지 않아서,- 무조건 새 객체를 생성해야 하는 상황!!!
spread 연산자
그래서 이렇게 작성해봤다.
1
2
3
4
5
const { postingHashtags, ...postingValues } = postingObj;
return {
...postingValues,
hashtags,
}
- 반환 값에 spread operator를 사용하지 않는 게 팀 컨벤션이었기 때문에, 이런 방법도 있다~ 하고 넘겼다.
결론
팀 회의 시간에 고민을 발표하며 얻은 결론이다.
- 타입스크립트는 개발자를 위한 일종의 도구이기 때문에, 타입 시스템은 컴파일 시점에만 작동하며 런타임에선 적용되지 않는다.
- 그래서
postingValues
에 원하는 값만 있을지는 모르는 일이다. 따라서 조금 번거롭더라도, 저렇게 명시적으로 필드를 적어주면 타입 추적도 쉬워지고 안전하다.
🤔 데이터소스가 사기인데 왜 레포 주입해서 쓰는거임?
의문
- Module 파일에
TypeOrmModule.forFeature([])
import 안 해도 그냥 써짐 - constructor 멤버변수로 Repo 줄줄이 달지 않고
DataSource
변수만 선언해준 뒤getRepository(${Entity})
하면 되어서 뭔가 깔끔하게 보임
결론
Repository
를 직접 주입해서 쓰는 경우- 도메인, 관심사 분리
- DI 컨테이너가 미리 의존성 주입 시켜놔서 쓰기 편하다.
- 테스트, 유지보수가 비교적 쉽다.
DataSource
를 사용하는 경우- 복잡한 쿼리를 작성하거나 특정 엔티티에 종속되지 않은 로직이 필요할 때 사용한다.
- 같은 패턴을 반복해야 해 번거로울 수 있다.
by GPT4o
DataSource
는 한 단계 상위 개념인 것 같아서, 지금 수준의 코드를 짤 땐 굳이 안 쓸 것 같다.
This post is licensed under CC BY 4.0 by the author.