JavaScript에서 TypeScript로 변경 중 이슈 해결

haerim·2024년 2월 6일

저번에 이어서 계속 프로젝트를 ts로 변경하고 있는 중에 몇 가지 에러를 마주해서 해결했다.

[ 이슈 1 ] TypeError: Router.use() requires a middleware function but got a Object

기존에 있던 routes 안의 user.js 를 user.ts로 바꾸고 importing 하는 과정 중 맞닥뜨린 에러다.
해당 에러는 app.ts에서 사용할 파일을 제대로 export하지 않아 생기는 에러이다.
나 같은 경우에는 export는 제대로 해놨는데, app.ts에서 import를 제대로 하지 않아서 생긴 에러였다.

기존 코드

const bookRouter = require("./routes/books");

나는 현재 js에서 ts로 변경하는 과정이기 때문에 allowJs:true를 해놓은 상태다.
그래서 ts 파일에서 js 파일을 가져오는 문법을 썼었는데, 해당 문법은 js 파일이 ts 파일로 바뀌는 순간 import 구문으로 변경해줘야 한다.

수정 코드

import router from "./routes/users";

...

app.use("/users", router);

이렇게 변경해주니까 무리 없이 인식했다

[ 이슈 2 ] Router 별명 이슈

이제 수정을 해놓고 보니 내 프로젝트는 현재 routes 폴더 내에 용도에 따라 route 파일이 구분이 되어 있다.
모든 파일은

export default router;

로 export 하고 있는데, 이러면 app.ts 내에서 이름이 겹쳐서 별명의 필요성이 생겼다.
그래서 app.ts 내 별명을 처음에 이렇게 바꿨다.


import { router as userRouter } from "./routes/users";

이러니까 이런 에러가 났다...

에러 내용을 읽어보니까 users 파일에 router 를 내보내지 않았다는데 분명 난 내보냈다...
그러면 users 파일에서 export를 할 때 별명을 줘보기로 했다.

자바스크립트 문법 소개하는 부분에서 모듈 내보내고 가져오기 부분 중 export as 부분을 참고해 작성했다. 참조 글

수정 코드 (export as 구문 사용)

  • users.ts

    export { router as userRouter };
    
  • app.ts

    import { userRouter } from "./routes/users";
    

이러면 잘 동작한다. 그런데 나는 export default 구문으로 작성하고 싶었다.

export default 구문을 사용하고 싶은 이유

애초에 users.ts 는 router 라는 객체 하나만 선언되어 있는 모듈이다.
그런데 users.ts에서 저런 식으로 작성하면 app.ts에서 import 해올 때, users 파일 안에 여러 개의 메소드가 들어가 있는 것처럼 보여서... 그게 마음에 들지 않았다.

그런데 export default { router as userRouter } 이런 식으로 작성하면 에러난다. (당연하지 없는 문법인데;)
그래서 좀 살펴봤다.

수정 코드 1 (export = 구문 사용)

처음에는 export =구문을 사용했다. 이 구문은 TypeScript에서 사용되는 모듈 시스템의 한 형태다.
이는 CommonJS 스타일에서의 module.exports와 유사하게 동작한다고 한다.

  • users.ts

    export = router;
    
  • app.ts

    import userRouter from "./routes/users";
    

이러면 또 무리 없이 동작하는데, 최신 TypeScript 버전에서는 보통 export default를 사용하는 것이 일반적이라고 한다.

그래 나 export default 사용하고 싶었다고!!!!!
하고 또 봤는데 그다지... 어렵지 않았다...

수정 코드 2 (export default 구문 사용)

  • users.ts

    export default router;
    
  • app.ts

    import userRouter from "./routes/users";
    

...? 잉?
물론 내가 React에서 이걸 안 한 건 아닌데, React는 Functional Component 이름을 그대로 export 해서 사용한 적이 많았다.
그래서 이런 식으로 아예 export할 때와 import 할 때의 이름이 다른 적이 처음이었다. 그래서 찾아봤다.

이게 가능한 이유는 TypeScrip의 특성이라고 한다.

TypeScript에서는 import 구문을 사용할 때, 가져오는 모듈의 이름을 외부에서 임의로 변경할 수 있습니다.
이것은 유연성을 제공하기 위한 TypeScript의 특징 중 하나입니다.

음 그렇군... TypeScript도 일정 부분에서는 flexible한 언어임을 다시 깨닫고 간다...

그래서 여기서 모든 이슈가 해결됐다!

혼자 궁금해짐

혹시... 그렇다면 users 파일 내에 존재하고 있는 걸 함수로 감싸서 내보낼 수도 있을까?


import express from "express";

const userRouter = () => {
    const router = express.Router();
    const {
        userJoin,
        userLogin,
        requestPasswordReset,
        passwordReset,
    } = require("../controller/userController");

    router.use(express.json());

    router.post("/join", userJoin);
    router.post("/login", userLogin);
    router.post("/reset", requestPasswordReset);
    router.put("/reset", passwordReset);

    return router;
};

export default userRouter;

이렇게 변경해봤는데 일단 코드 상으로는 문제가 없다는 투다. (nodemon이 잘 돌아가고 있음)
그렇다면 postman을 돌려볼 차례... 안 돌아간다.

이거 왜 안 되지?... 는

애초에 express.Router() 함수를 호출하여 새로운 라우터 객체를 생성하고, 이 객체를 내보내야 userRouter를 다른 파일에서 가져와서 라우팅을 적용할 수 있다고 한다. 즉, 함수 userRouter를 내보내는 것이 아니라 라우터 객체인 router를 내보내야 한다는 것.

express.Router()로 생성한 라우터 객체는 이미 그 자체로 미들웨어처럼 동작하며, 이 객체를 함수로 한 번 더 감싸는 경우 추가적인 이점이 없다고 한다.

그러니까 함수로 한 번 감싸서 내보낼 필요가 없었다. 그냥 뻘짓이었다 ㅠㅠ

하지만 알아가서 좋은 시간이었다... 굿

profile
멋진 프론트엔드 개발자가 되고 싶은

0개의 댓글