Skip to content

Commit daafc31

Browse files
committed
Merge branch 'dev' into 127-feedback-페이지-자잘한-버그-수정-및-스타일링
2 parents a42cf55 + c714d10 commit daafc31

29 files changed

+261
-150
lines changed
Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,7 @@
1-
import {
2-
Controller,
3-
Get,
4-
HttpCode,
5-
Logger,
6-
Param,
7-
Query,
8-
Req,
9-
Res,
10-
UseGuards,
11-
} from '@nestjs/common';
1+
import { Controller, Get, Param, Query, Req, Res, UseGuards } from '@nestjs/common';
122
import { AuthService } from '../service/auth.service';
133
import { Request, Response } from 'express';
14-
import { HTTP_STATUS, tokenCookieOptions, JWT_TYPE } from '@constant';
4+
import { tokenCookieOptions, JWT_TYPE } from '@constant';
155
import { JwtAuthGuard } from '../guard/jwt.guard';
166
import { JwtPayload } from '@types';
177

@@ -20,7 +10,6 @@ export class AuthController {
2010
constructor(private readonly authService: AuthService) {}
2111

2212
@Get('oauth/redirect/:type')
23-
@HttpCode(HTTP_STATUS.HTTP_REDIRECT)
2413
redirectOauthPage(@Param('type') type: string) {
2514
const pageUrl = this.authService.getSocialUrl(type);
2615
return { url: pageUrl };
@@ -32,7 +21,12 @@ export class AuthController {
3221
@Param('type') type: string,
3322
@Res() res: Response
3423
) {
24+
if (!authorizationCode) {
25+
res.redirect(process.env.CLIENT_ORIGIN_URL);
26+
return;
27+
}
3528
const user = await this.authService.socialStart({ type, authorizationCode });
29+
3630
const { accessToken, refreshToken } =
3731
this.authService.createAccessTokenAndRefreshToken(user);
3832

@@ -43,12 +37,10 @@ export class AuthController {
4337

4438
@UseGuards(JwtAuthGuard)
4539
@Get('login')
46-
@HttpCode(HTTP_STATUS.HTTP_OK)
4740
loginValidate(@Req() req: Request, @Res({ passthrough: true }) res: Response) {
4841
const { accessToken, refreshToken } = req.cookies;
4942
res.cookie(JWT_TYPE.ACCESS_TOKEN, accessToken, tokenCookieOptions);
5043
res.cookie(JWT_TYPE.REFRESH_TOKEN, refreshToken, tokenCookieOptions);
51-
return { statusCode: 200 };
5244
}
5345

5446
@UseGuards(JwtAuthGuard)
@@ -63,6 +55,5 @@ export class AuthController {
6355
logout(@Res({ passthrough: true }) res: Response) {
6456
res.clearCookie(JWT_TYPE.ACCESS_TOKEN);
6557
res.clearCookie(JWT_TYPE.REFRESH_TOKEN);
66-
return { statusCode: 200 };
6758
}
6859
}

backend/rest/src/auth/service/auth.service.spec.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,12 +153,11 @@ describe('AuthService', () => {
153153
});
154154

155155
const makeMockUser = (userInfo: UserInfo): UserEntity => {
156-
const { id, email, password, nickname, oauthType } = userInfo;
156+
const { id, email, password, oauthType } = userInfo;
157157
const userEntity = new JoinUserBuilder()
158158
.setId(id)
159159
.setEmail(email)
160160
.setPassword(password)
161-
.setNickname(nickname)
162161
.setOauthType(oauthType)
163162
.build();
164163
return userEntity;

backend/rest/src/auth/service/auth.service.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import {
22
accessTokenOptions,
3+
HTTP_ERROR_MSG,
34
OAUTH_TYPE,
45
refreshTokenOptions,
56
USER_REPOSITORY_INTERFACE,
67
} from '@constant';
7-
import { Inject, Injectable } from '@nestjs/common';
8+
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
89
import { UserInfo } from '@types';
910
import { UserRepository } from 'src/user/repository/user.repository';
1011
import { OauthNaverService } from './oauth/naver-oauth.service';
@@ -52,14 +53,13 @@ export class AuthService {
5253
async socialStart({ type, authorizationCode }: { type: string; authorizationCode: string }) {
5354
this.setOauthInstanceByType(type);
5455

55-
if (!authorizationCode) throw new Error('social 인증이 되지 않았습니다.');
56-
5756
const accessToken = await this.oauthInstance.getAccessTokenByAuthorizationCode(
5857
authorizationCode
5958
);
6059
const userSocialInfo = await this.oauthInstance.getSocialInfoByAccessToken(accessToken);
6160

6261
const user = await this.userRepository.saveUser(userSocialInfo as UserInfo);
62+
6363
return user;
6464
}
6565

@@ -97,6 +97,7 @@ export class AuthService {
9797
*/
9898
createAccessTokenAndRefreshToken(user: UserInfo) {
9999
const { id, email } = user;
100+
100101
const payload = new JwtPayloadBuiler().setId(id).setEmail(email).build();
101102

102103
const accessToken = this.createJwt({ payload, ...accessTokenOptions });
@@ -117,7 +118,7 @@ export class AuthService {
117118
this.oauthInstance = this.oauthKakaoService;
118119
break;
119120
default:
120-
throw new Error();
121+
throw new BadRequestException(HTTP_ERROR_MSG.UNKNOWN_OAUTH_TYPE_ERROR);
121122
}
122123
}
123124
}

backend/rest/src/auth/strategy/access-jwt.strategy.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@ import { ConfigService } from '@nestjs/config';
44
import { PassportStrategy } from '@nestjs/passport';
55
import { ExtractJwt, Strategy } from 'passport-jwt';
66
import { Request } from 'express';
7-
import { JwtPayload } from 'src/types/auth.type';
7+
import { JwtPayload, UserInfo } from 'src/types/auth.type';
8+
import { AuthService } from '../service/auth.service';
89

910
@Injectable()
1011
export class JwtAccessStrategy extends PassportStrategy(Strategy, 'jwt-access') {
11-
constructor(private readonly configService: ConfigService) {
12+
constructor(
13+
private readonly configService: ConfigService,
14+
private readonly authService: AuthService
15+
) {
1216
super({
1317
ignoreExpiration: false,
1418
jwtFromRequest: ExtractJwt.fromExtractors([
@@ -23,6 +27,15 @@ export class JwtAccessStrategy extends PassportStrategy(Strategy, 'jwt-access')
2327
}
2428

2529
async validate(req: Request, payload: JwtPayload) {
30+
const { id, email } = payload;
31+
const { accessToken, refreshToken } = this.authService.createAccessTokenAndRefreshToken({
32+
id,
33+
email,
34+
} as UserInfo);
35+
36+
req.cookies.accessToken = accessToken;
37+
req.cookies.refreshToken = refreshToken;
38+
2639
return payload;
2740
}
2841
}

backend/rest/src/auth/strategy/refresh-jwt.strategy.ts

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
import { accessTokenOptions, JWT_ENV } from '@constant';
1+
import { JWT_ENV } from '@constant';
22
import { Injectable } from '@nestjs/common';
33
import { ConfigService } from '@nestjs/config';
44
import { PassportStrategy } from '@nestjs/passport';
55
import { ExtractJwt, Strategy } from 'passport-jwt';
66
import { Request } from 'express';
77
import { AuthService } from '../service/auth.service';
8-
import { JwtPayload } from 'src/types/auth.type';
9-
import { JwtPayloadBuiler } from 'src/auth/dto/create-jwt.builder';
8+
import { JwtPayload, UserInfo } from 'src/types/auth.type';
109

1110
@Injectable()
1211
export class JwtRefreshStrategy extends PassportStrategy(Strategy, 'jwt-refresh') {
@@ -17,7 +16,7 @@ export class JwtRefreshStrategy extends PassportStrategy(Strategy, 'jwt-refresh'
1716
super({
1817
ignoreExpiration: false,
1918
jwtFromRequest: ExtractJwt.fromExtractors([
20-
(req) => {
19+
(req: Request) => {
2120
const token = req?.cookies.refreshToken;
2221
return token ?? null;
2322
},
@@ -29,13 +28,13 @@ export class JwtRefreshStrategy extends PassportStrategy(Strategy, 'jwt-refresh'
2928

3029
async validate(req: Request, payload: JwtPayload): Promise<JwtPayload> {
3130
const { id, email } = payload;
32-
const createJwtPayload = new JwtPayloadBuiler().setId(id).setEmail(email).build();
33-
const accessToken = this.authService.createJwt({
34-
payload: createJwtPayload,
35-
...accessTokenOptions,
36-
});
31+
const { accessToken, refreshToken } = this.authService.createAccessTokenAndRefreshToken({
32+
id,
33+
email,
34+
} as UserInfo);
3735

3836
req.cookies.accessToken = accessToken;
37+
req.cookies.refreshToken = refreshToken;
3938

4039
return payload;
4140
}

backend/rest/src/config/env.config.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export const envConfig: ConfigModuleOptions = {
55
isGlobal: true, // 환경 변수를 전역으로 사용
66
envFilePath: process.env.NODE_ENV === 'dev' ? '.env' : '.env.test',
77
// 루트 경로에서 .env 사용 (cross-env로 환경에 따른 .env 적용도 가능)
8+
// public에 올리면 env 파일의 변수 목록이 공개되어 안 좋지 않을까..
89
validationSchema: Joi.object({
910
DB_USER: Joi.string().required(),
1011
DB_PASSWORD: Joi.string().required(),
@@ -13,5 +14,15 @@ export const envConfig: ConfigModuleOptions = {
1314
JWT_ACCESS_TOKEN_EXPIRATION_TIME: Joi.string().required(),
1415
JWT_REFRESH_TOKEN_SECRET: Joi.string().required(),
1516
JWT_REFRESH_TOKEN_EXPIRATION_TIME: Joi.string().required(),
17+
18+
NAVER_CLIENT_ID: Joi.string().required(),
19+
NAVER_CLIENT_SECRET: Joi.string().required(),
20+
21+
KAKAO_CLIENT_ID: Joi.string().required(),
22+
KAKAO_CLIENT_SECRET: Joi.string().required(),
23+
24+
CLIENT_ORIGIN_URL: Joi.string().required(),
25+
26+
OBJECT_STORAGE_ENDPOINT: Joi.string().required(),
1627
}),
1728
};
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
export enum HTTP_STATUS {
2-
HTTP_OK = 200,
3-
HTTP_REDIRECT = 301,
1+
export enum HTTP_ERROR_MSG {
2+
UNKNOWN_OAUTH_TYPE_ERROR = '알 수 없는 Oauth Type입니다.',
3+
OAUTH_AUTHENTICATION_CANCEL = 'social 인증이 되지 않았습니다.',
4+
NOT_FOUND_TARGET_IN_DATABASE = 'DB에 삭제할 대상이 없습니다.',
5+
NOT_FOUND_MATCHED_DOCS = '매치되는 docs가 없습니다.',
46
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import {
2+
ArgumentsHost,
3+
Catch,
4+
ExceptionFilter,
5+
HttpException,
6+
InternalServerErrorException,
7+
Logger,
8+
} from '@nestjs/common';
9+
import { Request, Response } from 'express';
10+
11+
@Catch()
12+
export class HttpExceptionFilter implements ExceptionFilter {
13+
catch(exception: Error, host: ArgumentsHost) {
14+
const logger = new Logger('EXCEPTION');
15+
16+
const ctx = host.switchToHttp();
17+
const res = ctx.getResponse<Response>();
18+
const req = ctx.getRequest<Request>();
19+
20+
if (!(exception instanceof HttpException)) {
21+
exception = new InternalServerErrorException();
22+
}
23+
24+
const { statusCode, message }: any = (exception as HttpException).getResponse();
25+
26+
logger.error(`[Request URL] ${req.url}`);
27+
logger.error(`[Exception Time] ${new Date().toISOString()}`);
28+
logger.error(`[Exception Message] ${message}`);
29+
logger.error(`[Exception Stack] ${exception.stack}`);
30+
31+
const response = {
32+
message: message,
33+
stack: exception.stack,
34+
};
35+
36+
res.status(statusCode).json(response);
37+
}
38+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { Injectable, NestInterceptor, ExecutionContext, CallHandler, Logger } from '@nestjs/common';
2+
import { Observable } from 'rxjs';
3+
import { tap, map } from 'rxjs/operators';
4+
5+
@Injectable()
6+
export class RestInterceptor implements NestInterceptor {
7+
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
8+
const logger = new Logger('REST API');
9+
const ctx = context.switchToHttp();
10+
const req = ctx.getRequest<Request>();
11+
12+
const start = new Date().getTime();
13+
return next
14+
.handle()
15+
.pipe(
16+
tap(() => {
17+
const end = new Date();
18+
logger.log(`[Request URL] ${req.url}`);
19+
logger.log(`[Process Time] ${end.getTime() - start}ms`);
20+
})
21+
)
22+
.pipe(map((result) => ({ data: result })));
23+
}
24+
}

backend/rest/src/interview/controller/interview.controller.ts

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,28 +14,23 @@ export class InterviewController {
1414
@Post('docs')
1515
async createInterviewDocs(@Req() req: Request, @Body() docsRequestDto: DocsRequestDto) {
1616
const payload = req.user as JwtPayload;
17-
console.log(payload);
1817

1918
await this.interviewService.createInterviewDocs({ userId: payload.id, docsRequestDto });
20-
return { statusCode: 201 };
19+
return {};
2120
}
2221

2322
@Post('feedback')
2423
async createFeedback(@Req() req: Request, @Body() feedbackRequestDto: FeedbackRequestDto) {
2524
const payload = req.user as JwtPayload;
2625
await this.interviewService.saveFeedback({ userId: payload.id, feedbackRequestDto });
27-
return { statusCode: 201 };
26+
return {};
2827
}
2928

3029
@Get('docs/:docsUUID')
31-
async getInterviewDocs(@Req() req: Request, @Param('docsUUID') docsUUID: string) {
32-
const payload = req.user as JwtPayload;
33-
const interviewDocs = await this.interviewService.getInterviewDocs({
34-
userId: payload.id,
35-
docsUUID,
36-
});
30+
async getInterviewDocs(@Param('docsUUID') docsUUID: string) {
31+
const interviewDocs = await this.interviewService.getInterviewDocs(docsUUID);
3732

38-
return { statusCode: 201, data: interviewDocs };
33+
return interviewDocs;
3934
}
4035

4136
@Get('docs-list')
@@ -45,6 +40,7 @@ export class InterviewController {
4540
userId: payload.id,
4641
roomUUID,
4742
});
48-
return { statusCode: 200, data: interviewDocsList };
43+
44+
return interviewDocsList;
4945
}
5046
}

0 commit comments

Comments
 (0)