프로젝트를 타입 스크립트로 마이그레이션 하는 중...
서비스단에서 바인딩 요소 'commentId'에 암시적으로 'any' 형식이 있습니다. 라는 오류가 났다
어디가 잘못되었는지 코드를 봅시다
//service
commentUpdate = async ({ commentId, content, userId })
//❌ 오류! 바인딩 요소 'commentId'에 암시적으로 'any' 형식이 있습니다.
=> {
if (content.trim().length == 0)
throw new AppError("내용을 입력해주세요", 400);
const comment = await
...
우선 나는 컨트롤러에서 들어오는 리퀘스트 타입을 커스텀 타입으로 확장해서 제작했다
들어오는 타입들은 전부 RequestBodyT,RequestParamsT 로 타입을 지정해 한번에 처리하려했으나...
서비스에서 타입을 불러올 수 없어서 실패!
// 컨트롤러
commentList = async (req: AuthRequest, res: Response, next: NextFunction) => {
...
//AuthRequest
export interface AuthRequest<P = RequestParamsT, Q = {}, B = RequestBodyT>
extends Request<P, Q, B> {
user?: Users;
}
//type
export interface RequestBodyT {
user?: Users;
title?: string;
...
}
export interface RequestParamsT {
userId?: number;
...
}
이유는 간단하다
함수에서 데이터를 직접 꺼내서 변수로 받으면 변수들이 어떤 타입인지 정확히 알 수 없기때문.....
function 함수1 (변수1:number){
const 변수2:number = 변수1
console.log(변수1,"변수1")
함수2(변수2)
}
function 함수2 (변수2) { //❌ 거 타입이 뭡니까?
console.log(변수2,"변수2")
}
함수1(33333)
function 함수1 (변수1:number){
const 변수2:number = 변수1
console.log(변수1,"변수1")
함수2(변수2)
}
function 함수2 (변수2:number) { //✅ 변수2는 number로군
console.log(변수2,"변수2")
}
함수1(33333)

좀 멍청했지만 뭐... 그러면서...배우는것이겠지요...
궁금해서 해봤는데 이것도 안된다 타입지정해주자
function 함수1 (변수1:number){
console.log(변수1,"함수1의 변수1")
함수2(변수1)
}
function 함수2 (변수1) { //❌ 거 타입이 뭡니까?
console.log(변수1,"함수2의 변수1")
}
함수1(33333)
function 함수1 (변수1:number){
const 변수2:number = 변수1
console.log(변수1,"변수1")
함수2(변수2)
}
function 함수2 (변수2:number) { //변수2는 number 타입!
console.log(변수2,"변수2")
}
함수1(33333)
하지만 매개변수가 많아지면 불편함..
하나하나 써주기도 함들다...
function 함수1 (변수1:number,변수2:number,변수3:string,변수4:string){
함수2(변수1,변수2,변수3,변수4)
}
function 함수2 (변수1:number,변수2:number,변수3:string,변수4:string){
console.log(변수1,변수2,변수3,변수4)
}
const 변수1 = 1
const 변수2 = 2
const 변수3 = "3"
const 변수4 = "4"
....
interface 변수들타입 {
변수1: number;
변수2: number;
변수3: string;
변수4: string;
}
function 함수1(변수들: 변수들타입) {
함수2(변수들);
}
function 함수2(변수들: 변수들타입) {
console.log(변수들.변수1, 변수들.변수2, 변수들.변수3, 변수들.변수4);
}
const 변수1 = 1
const 변수2 = 2
const 변수3 = "3"
const 변수4 = "4"
함수1({ 변수1, 변수2, 변수3, 변수4});
이렇게 정리해두면 저 변수들 타입을 유지보수하기 편하다!
이걸 고급스럽게 DTO라고 부르는데 (Data Transfer Object)
간단하게는 그냥 데이터 주고받을때 타입 정의하는 걸 DTO라고 한다
//서비스 코드
commentUpdate = async ({ commentId, content, userId }) ...
commentDelete = async ({ commentId, userId }) ...
commentCreate = async ({ content, postId, userId }) ...
위의 매개변수를 받기 위한 DTO를 만들어보자
export interface CommentUpdateDto {
commentId: number;
content: string;
userId: number;
}
export interface CommentDeleteDto {
commentId: number;
userId: number;
}
export interface CommentCreateDto {
content: string;
postId: number;
userId: number;
}
보니 중복되는 부분이 많다
어떻게 해결할 수 있을까

중복되는 부분을 따로 빼고
나머지 dto들을 확장시켜 써보기로했다
export interface CommentBaseDto {
content: string;
userId: number;
}
// 댓글 수정 DTO
export interface CommentUpdateDto extends CommentBaseDto {
commentId: number;
}
// 댓글 삭제 DTO
export interface CommentDeleteDto {
commentId: number;
userId: number;
}
// 댓글 생성 DTO
export interface CommentCreateDto extends CommentBaseDto {
postId: number;
}
이러면 코드는 좀 길지만 중복코드로 인한 실수를 예방할수있고
나중에 매개변수가 늘어나도 편하게 쓸 수 있다!
++ 추가 사항
이전에 리퀘스트를 받아올때 사용했던 AuthRequest타입에 제네릭으로 적용하여 기존에 적용했던 RequestBodyT,RequestParamsT 의 모호함을 개선했다
// 기존 제네릭 타입
export interface RequestBodyT {
user?: Users;
title?: string;
content?: string;
nickname?: string;
password?: string;
postId?: number;
}
export interface RequestParamsT {
userId?: number;
postId?: number;
commentId?: number;
}
//매개변수로 사용하던 타입들을 AuthRequest 제네릭으로 사용
export interface PostBaseDto {
userId: number;
}
export interface UpdatePostDto extends PostBaseDto {
title: string;
content: string;
postId: number;
}
export interface DeletePostDto extends PostBaseDto {
postId: number;
}
export interface CreatePostDto extends PostBaseDto {
title: string;
content: string;
}
////
commentUpdate = async (
req: AuthRequest<CommentUpdateDto, {}, CommentUpdateDto>,
res: Response,
next: NextFunction
) => {
코드가 줄고 보기에 더 편해졌다!