๐ŸŽ‰ berenickt ๋ธ”๋กœ๊ทธ์— ์˜จ ๊ฑธ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค. ๐ŸŽ‰
Back
NestJs
16-Custom Decorator

1. AccessTokenGuard ์ ์šฉ

post ์ž‘์„ฑ์€ ๋กœ๊ทธ์ธ์„ ํ•ด์•ผ์ง€๋งŒ, ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฐ”๊ฟ”๋ณด๊ฒ ๋‹ค.

posts.controller.ts
1
// posts.controller.ts ์ƒ๋žต
2
@Post()
3
@UseGuards(AccessTokenGuard)
4
postPosts(
5
@Request() req: any,
6
@Body('title') title: string, //
7
@Body('content') content: string,
8
// ๊ธฐ๋ณธ๊ฐ’์„ true๋กœ ์„ค์ •ํ•˜๋Š” ํŒŒ์ดํ”„
9
// @Body('isPublic', new DefaultValuePipe(true)) isPublic: boolean,
10
) {
11
const authorId = req.user.id
12
return this.postsService.createPost(authorId, title, content)
13
}
  • ๋กœ๊ทธ์ธํ•œ ์‚ฌ์šฉ์ž๋งŒ ํฌ์ŠคํŠธ์ž‘์„ฑ API๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฐ”๊พธ๋ ค๋ฉด,
  • AccessTokenGuard์„ ์ ์šฉํ•ด์ฃผ๋ฉด ๋œ๋‹ค.
  • cf. ์ด๋ฅผ private route๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

๊ทธ๋Ÿฌ๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๋””ํŽœ๋˜์‹œ ์—๋Ÿฌ๊ฐ€ ๋‚˜์˜ฌ ๊ฒƒ์ด๋‹ค.

1
Nest can't resolve dependencies of the AccessTokenGuard
2
(?, UsersService). Please make sure that the argument AuthService
3
at index [0] is available in the PostsModule context.

posts ๋ชจ๋“ˆ์— AuthService, UsersService์™€ JwtModule.register(), UsersModel๊นŒ์ง€ ์ถ”๊ฐ€ํ•ด์ค€๋‹ค.

posts.module.ts
1
// posts.module.ts ์ƒ๋žต
2
@Module({
3
imports: [
4
JwtModule.register({}), // ์ถ”๊ฐ€
5
TypeOrmModule.forFeature([PostsModel, UsersModel]), // ์ถ”๊ฐ€
6
],
7
controllers: [PostsController],
8
providers: [PostsService, AuthService, UsersService], // ์ถ”๊ฐ€
9
})
10
export class PostsModule {}

2. User ์ปค์Šคํ…€ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ ์ƒ์„ฑ

๋ชจ๋“  ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” ๋Œ€๋ฌธ์ž๋กœ ์‹œ์ž‘ํ•œ๋‹ค.

users/decorator/user.decorator.ts ํŒŒ์ผ์„ ๋งŒ๋“ ๋‹ค.

users/decorator/user.decorator.ts
1
import { ExecutionContext, InternalServerErrorException, createParamDecorator } from '@nestjs/common'
2
3
export const User = createParamDecorator((data, context: ExecutionContext) => {
4
const req = context.switchToHttp().getRequest()
5
const user = req.user
6
7
if (!user) {
8
throw new InternalServerErrorException(
9
'User ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” AccessTokenGuard์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Request์— user ํ”„๋กœํผํ‹ฐ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค!',
10
)
11
}
12
13
return user
14
})

posts ์ปจํŠธ๋กค๋Ÿฌ์— User ์ปค์Šคํ…€ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์ ์šฉํ•œ๋‹ค.

posts.controller.ts
1
// posts.controller.ts ์ƒ๋žต
2
@Post()
3
@UseGuards(AccessTokenGuard)
4
postPosts(
5
@User() user: UsersModel, // โœ… User ์ปค์Šคํ…€ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์ ์šฉ
6
@Body('title') title: string,
7
@Body('content') content: string,
8
// ๊ธฐ๋ณธ๊ฐ’์„ true๋กœ ์„ค์ •ํ•˜๋Š” ํŒŒ์ดํ”„
9
// @Body('isPublic', new DefaultValuePipe(true)) isPublic: boolean,
10
) {
11
return this.postsService.createPost(user.id, title, content)
12
}

auth ์„œ๋น„์Šค์— ํ† ํฐ ๊ฒ€์ฆ์— try~catch ๋ฌธ์„ ์ ์šฉํ•œ๋‹ค.

auth.service.ts
1
// auth.service.ts ์ƒ๋žต
2
/*** ํ† ํฐ ๊ฒ€์ฆ
3
*
4
*/
5
verifyToken(token: string) {
6
try {
7
return this.jwtService.verify(token, {
8
secret: JWT_SECRET,
9
})
10
} catch (err) {
11
throw new UnauthorizedException('ํ† ํฐ์ด ๋งŒ๋ฃŒ๋๊ฑฐ๋‚˜ ์ž˜๋ชป๋œ ํ† ํฐ์ž…๋‹ˆ๋‹ค.')
12
}
13
}

ํฌ์ŠคํŠธ๋งจ์—์„œ authorId ์—†์ด, ํ† ํฐ์—์„œ ์œ ์ € ์ •๋ณด๋ฅผ ๊บผ๋‚ด ํฌ์ŠคํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฑธ ํ™•์ธํ•œ๋‹ค.


3. ์ปค์Šคํ…€ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์˜ data ํŒŒ๋ผ๋ฏธํ„ฐ ์‚ฌ์šฉ

data ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์‚ฌ์šฉํ•ด์„œ, ์‚ฌ์šฉ์ž์˜ ํŠน์ • ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ๋„ฃ์–ด์ฃผ๋ฉด, ํ•ด๋Œฑ๋˜๋Š” ํ”„๋กœํผํ‹ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ฒŒ ํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค.

users/decorator/user.decorator.ts
1
import { ExecutionContext, InternalServerErrorException, createParamDecorator } from '@nestjs/common'
2
import { UsersModel } from '../entities/users.entity'
3
4
export const User = createParamDecorator(
5
(
6
data: keyof UsersModel | undefined, //
7
context: ExecutionContext,
8
) => {
9
const req = context.switchToHttp().getRequest()
10
const user = req.user as UsersModel
11
12
if (!user) {
13
throw new InternalServerErrorException(
14
'User ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” AccessTokenGuard์™€ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. Request์— user ํ”„๋กœํผํ‹ฐ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค!',
15
)
16
}
17
18
if (data) {
19
return user[data]
20
}
21
22
return user
23
},
24
)

posts ์ปจํŠธ๋กค๋Ÿฌ์— User ์ปค์Šคํ…€ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋ฅผ ์ ์šฉํ•œ๋‹ค.

posts.controller.ts
1
// posts.controller.ts ์ƒ๋žต
2
@Post()
3
@UseGuards(AccessTokenGuard)
4
postPosts(
5
@User('id') userId: number, // โœ… data ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ด์šฉํ•ด ํŠน์ • ํŒŒ๋ผ๋ฏธํ„ฐ๋งŒ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.
6
@Body('title') title: string, //
7
@Body('content') content: string,
8
// ๊ธฐ๋ณธ๊ฐ’์„ true๋กœ ์„ค์ •ํ•˜๋Š” ํŒŒ์ดํ”„
9
// @Body('isPublic', new DefaultValuePipe(true)) isPublic: boolean,
10
) {
11
return this.postsService.createPost(userId, title, content)
12
}