포스트

corou : 의존성 주입 오류

의존성 주입

1
2
3
Error resolving UserController: Error: Cannot inject the dependency "userService" at position #0 of "UserController" constructor. Reason:
    Cannot inject the dependency at position #0 of "UserService" constructor. Reason:
        Attempted to resolve unregistered dependency token: "UserRepository"

이 오류를 토요일에 약 5시간동안 고치지 못했다. 오류를 읽어보면 userRepository가 register되지 않아 이를 의존성으로 갖는 userService를 주입을 못하는 상황이 계속 발생하는 듯 했다.

그렇지만, 아래를 보면 분명 register을 했는데 계속 이런 일이 발생하는 것이었다:

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
import 'reflect-metadata';
import { DataSource } from 'typeorm';
import dotenv from 'dotenv';
import { container } from 'tsyringe';
import { REPOSITORY_TOKENS } from './constants';

import { User } from '../entities/user.entity';

import { UserService } from '../services/user.service';

dotenv.config({ path: '.env' });

const { DB_DATABASE, DB_USER, DB_HOST, DB_PASSWORD } = process.env;

if (!DB_DATABASE || !DB_USER || !DB_HOST || !DB_PASSWORD) {
    throw new Error('Missing database configuration environment variables.');
}

export const AppDataSource = new DataSource({
    type: 'mysql',
    host: DB_HOST,
    port: 3306,
    username: DB_USER,
    password: DB_PASSWORD,
    database: DB_DATABASE,
    synchronize: false,
    logging: false,
    entities: [User, Address, Routine, Item, SkinAttribute, ItemOrder, OrderDetail, Review],
});


export const initializeDatabase = async () => {
    try {
        await AppDataSource.initialize();
        console.log('Database connection established successfully');

        const repositories = [
            { name: 'UserRepository', entity: User },
            // other entities
        ];

        for (const repo of repositories) {
            container.register(repo.name, { useValue: AppDataSource.getRepository(repo.entity) });
        }

        container.register(UserService, { useClass: UserService });
        // other services

    } catch (error) {
        console.error('Error initializing database:', error);
    }
}

이 문제를 해결하기 위해 여러 코드를 상당 부분 간소화했는데, 그럼에도 계속 같은 오류가 발생하니 AI를 통해 문제점을 파악하고자 했지만 이 또한 별 소용 없었다.

콜스택을 트레이싱 하다 보니 user.router.ts가 문제인 듯 하여 이를 유심히 봤다.

주입 컨테이너가 제대로 설정이 되지 않아 register가 되지 않는 느낌이 컸는데, 분명 register 아래에 콘솔을 찍어보면 제대로 register을 했다는 로그만 남을 뿐이었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//server.ts
import app from './app';
import userRouter from './routes/user.router';
import { initializeDatabase } from './config/ormconfig';

const PORT = process.env.PORT || 3000;

initializeDatabase().then(() => {
    app.use('/api/user', userRouter);

    app.get('/', (req, res) => {
        res.send('Hello World');
    });

    app.listen(PORT, () => {
        console.log(`Server is running on port ${PORT}`);
    });
}).catch((error) => {
    console.error('Error starting the server:', error);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// ormconfig.ts
export const initializeDatabase = async () => {
    try {
        await AppDataSource.initialize();
        console.log('Database connection established successfully');

        const repositories = [
            { name: REPOSITORY_TOKENS.UserRepository, entity: User },
            { name: REPOSITORY_TOKENS.AddressRepository, entity: Address },
            { name: REPOSITORY_TOKENS.RoutineRepository, entity: Routine },
            { name: REPOSITORY_TOKENS.ItemRepository, entity: Item },
            { name: REPOSITORY_TOKENS.SkinAttributeRepository, entity: SkinAttribute },
            { name: REPOSITORY_TOKENS.ItemOrderRepository, entity: ItemOrder },
            { name: REPOSITORY_TOKENS.OrderDetailRepository, entity: OrderDetail },
            { name: REPOSITORY_TOKENS.ReviewRepository, entity: Review },
        ];

        for (const repo of repositories) {
            container.register(repo.name, { useValue: AppDataSource.getRepository(repo.entity) });
        }
    } catch (error) {
        console.error('Error initializing database:', error);
    }
}

진입점 (server.ts)에서 ormconfig에서 만들어둔 initializeDatabase.then()을 사용해서 순차적으로 데이터베이스 연결 -> 서버 오픈 순서로 작성했는데도 문제가 생겼다.

user.router.ts에서 UserController가 resolve될 때, router를 import한 시점에 바로 controller를 resolve해서 생긴 문제였다.

이 문제를 고치기 위해 많은 시도를 거친 후 드디어 문제를 잡았다.

1
2
3
4
5
6
7
8
9
import { Router } from 'express';
import { UserController } from '../controllers/user.controller';

const router = Router();
const userController = new UserController();

router.post('/register', userController.createUser);

export default router;

모듈 형태

모듈 형태로 되어있던 라우터들을 함수형으로 바꾸니 해결이 되었다. 모듈 형태의 코드는 import 될 때 한 번 실행되기 때문에 UserController의 인스턴스를 생성하는데, 이 시점에 엔터티 레포지토리들이 등록이 안되어 있었기에 생긴 문제였다.

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
//routes.ts
import { Express } from 'express';
import { setupUserRouter } from './user.router';
// import routineRouter from './routine.router';

export function setupRoutes(app: Express) {
    app.use('/api/user', setupUserRouter());
    
    // app.use('/api/routine', setupRoutineRouter());
}

//user.router.ts
import { Router } from 'express';
import { container } from 'tsyringe';
import { UserController } from '../controllers/user.controller';
import { authMiddleware } from '../middlewares/auth.middleware';

export function setupUserRouter(): Router {
    const router = Router();
    const userController = container.resolve(UserController);

    router.get('/check/:username', (req, res) => userController.checkUsername(req, res));
    router.get('/self', authMiddleware, (req, res) => userController.getSelf(req, res));
    router.get('/:user_key', (req, res) => userController.getUserByKey(req, res));
    router.get('/', (req, res) => userController.getAllUsers(req, res));
    router.post('/register', (req, res) => userController.createUser(req, res));
    router.post('/login', (req, res) => userController.loginUser(req, res));
    router.post('/:user_key/address', authMiddleware, (req, res) => userController.addAddress(req, res));
    router.put('/:user_key/address', authMiddleware, (req, res) => userController.updateAddress(req, res));

    return router;
}

이렇게 난관(…)을 거치고 API들을 postman을 통해 테스트하니 잘 작동하는 것을 확인했다.

logintest

succ

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.