From ddf1f0e230faf7db82120c19b682b650107c2022 Mon Sep 17 00:00:00 2001 From: jeongmin Date: Sat, 2 Dec 2023 18:19:07 +0900 Subject: [PATCH 01/16] =?UTF-8?q?feat:=20=EC=97=B0=EB=8F=84=EB=B3=84=20?= =?UTF-8?q?=EB=AA=A8=EC=96=91=20=EC=88=9C=EC=9C=84=20API=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - stat.shapes.dto 생성 - stat/shapes-rank/:year GET API 구현 - queryBuilder에서 take 함수를 limit 함수로 변경 - getFormatResult에서 count를 number로 형변환 - statService에서 중복된 부분 모듈화 --- BE/src/stat/dto/stat.shapes.dto.ts | 8 ++++++ BE/src/stat/stat.controller.ts | 9 ++++++ BE/src/stat/stat.service.ts | 46 ++++++++++++++++++++++-------- 3 files changed, 51 insertions(+), 12 deletions(-) create mode 100644 BE/src/stat/dto/stat.shapes.dto.ts diff --git a/BE/src/stat/dto/stat.shapes.dto.ts b/BE/src/stat/dto/stat.shapes.dto.ts new file mode 100644 index 0000000..cbdb097 --- /dev/null +++ b/BE/src/stat/dto/stat.shapes.dto.ts @@ -0,0 +1,8 @@ +export class ShapeInfoDto { + uuid: number; + count: number; +} + +export class StatShapeDto { + [key: string]: ({ rank: number } & ShapeInfoDto) | {}; +} diff --git a/BE/src/stat/stat.controller.ts b/BE/src/stat/stat.controller.ts index 6853d1b..5535d6e 100644 --- a/BE/src/stat/stat.controller.ts +++ b/BE/src/stat/stat.controller.ts @@ -10,6 +10,7 @@ import { JwtAuthGuard } from "src/auth/guard/auth.jwt-guard"; import { User } from "src/auth/users.entity"; import { StatService } from "./stat.service"; import { StatTagDto } from "./dto/stat.tags.dto"; +import { StatShapeDto } from "./dto/stat.shapes.dto"; @Controller("stat") @UseGuards(JwtAuthGuard) @@ -23,4 +24,12 @@ export class StatController { ): Promise { return this.statService.getTopThreeTagsByUser(year, user.id); } + + @Get("/shapes-rank/:year") + async getShapesRank( + @Param("year", ParseIntPipe) year: number, + @GetUser() user: User, + ): Promise { + return this.statService.getTopThreeShapesByUser(year, user.id); + } } diff --git a/BE/src/stat/stat.service.ts b/BE/src/stat/stat.service.ts index d571559..f63c254 100644 --- a/BE/src/stat/stat.service.ts +++ b/BE/src/stat/stat.service.ts @@ -1,6 +1,7 @@ import { Injectable } from "@nestjs/common"; import { Diary } from "src/diaries/diaries.entity"; import { StatTagDto, TagInfoDto } from "./dto/stat.tags.dto"; +import { ShapeInfoDto, StatShapeDto } from "./dto/stat.shapes.dto"; @Injectable() export class StatService { @@ -8,36 +9,57 @@ export class StatService { year: number, userId: number, ): Promise { - const result: TagInfoDto[] = await this.fetchTopThreeTagsByUser( + const result: TagInfoDto[] = await this.fetchTopThreeByUser( year, userId, + "tags", + ["tags.name", "tags.id"], + ["tags.name as tag", "tags.id as id"], ); return this.getFormatResult(result); } - private async fetchTopThreeTagsByUser( + async getTopThreeShapesByUser( year: number, userId: number, - ): Promise { - return await Diary.createQueryBuilder("diary") - .select("tags.name", "tag") - .addSelect("tags.id", "id") + ): Promise { + const result: ShapeInfoDto[] = await this.fetchTopThreeByUser( + year, + userId, + "shape", + ["shape.uuid"], + ["shape.uuid as uuid"], + ); + return this.getFormatResult(result); + } + + private async fetchTopThreeByUser( + year: number, + userId: number, + joinTable: string, + groupByFields: any, + selectFields: any, + ): Promise { + return Diary.createQueryBuilder("diary") + .select(selectFields) .addSelect("COUNT(*)", "count") - .innerJoin("diary.tags", "tags") + .innerJoin(`diary.${joinTable}`, joinTable) .where("diary.user = :userId", { userId }) .andWhere("YEAR(diary.date) = :year", { year }) - .groupBy("tags.name") - .addGroupBy("tags.id") + .groupBy(groupByFields) .orderBy("count", "DESC") - .take(3) + .limit(3) .getRawMany(); } - private getFormatResult(result: TagInfoDto[]): StatTagDto { + private getFormatResult( + result: TagInfoDto[] | ShapeInfoDto[], + ): StatTagDto | StatShapeDto { const keys = ["first", "second", "third"]; const formattedResult = {}; - result.forEach((item, index) => { + result.forEach((item: TagInfoDto | ShapeInfoDto, index: number) => { const rank = index + 1; + item.count = Number(item.count); formattedResult[keys[index]] = { rank, ...item }; }); From c632c8995b646a2d6ab636e3cf11d47de14d2b56 Mon Sep 17 00:00:00 2001 From: jeongmin Date: Sat, 2 Dec 2023 18:44:00 +0900 Subject: [PATCH 02/16] =?UTF-8?q?test:=20getTopThreeShapesByUser=20?= =?UTF-8?q?=EC=A0=95=EC=83=81=20=EC=9A=94=EC=B2=AD=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/test/int/stat.service.int-spec.ts | 44 +++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/BE/test/int/stat.service.int-spec.ts b/BE/test/int/stat.service.int-spec.ts index a51fc79..8eaa153 100644 --- a/BE/test/int/stat.service.int-spec.ts +++ b/BE/test/int/stat.service.int-spec.ts @@ -27,7 +27,7 @@ describe("StatService 통합 테스트", () => { groupBy: jest.fn().mockReturnThis(), addGroupBy: jest.fn().mockReturnThis(), orderBy: jest.fn().mockReturnThis(), - take: jest.fn().mockReturnThis(), + limit: jest.fn().mockReturnThis(), getRawMany: jest.fn().mockResolvedValue([ { tag: "태그1", id: 1, count: 10 }, { tag: "태그2", id: 2, count: 9 }, @@ -48,4 +48,46 @@ describe("StatService 통합 테스트", () => { }); }); }); + + describe("getTopThreeShapesByUser 메서드", () => { + it("메서드 정상 요청", async () => { + const year = 2023; + const userId = 1; + + const mockQueryBuilder = { + select: jest.fn().mockReturnThis(), + addSelect: jest.fn().mockReturnThis(), + innerJoin: jest.fn().mockReturnThis(), + where: jest.fn().mockReturnThis(), + andWhere: jest.fn().mockReturnThis(), + groupBy: jest.fn().mockReturnThis(), + addGroupBy: jest.fn().mockReturnThis(), + orderBy: jest.fn().mockReturnThis(), + limit: jest.fn().mockReturnThis(), + getRawMany: jest.fn().mockResolvedValue([ + { uuid: "5d94024c-0f41-4e42-b766-a4f298766f67", count: 6 }, + { uuid: "5c670cee-f1e1-42ee-9b97-1ff5f561049e", count: 5 }, + ]), + }; + + jest + .spyOn(Diary, "createQueryBuilder") + .mockReturnValue(mockQueryBuilder as any); + + const result = await service.getTopThreeShapesByUser(year, userId); + + expect(result).toEqual({ + first: { + rank: 1, + uuid: "5d94024c-0f41-4e42-b766-a4f298766f67", + count: 6, + }, + second: { + rank: 2, + uuid: "5c670cee-f1e1-42ee-9b97-1ff5f561049e", + count: 5, + }, + }); + }); + }); }); From c5cd36eb0be5a9eb7dc9a428cba00904f5fb2bcf Mon Sep 17 00:00:00 2001 From: JoonSoo-Kim Date: Sun, 3 Dec 2023 21:01:55 +0900 Subject: [PATCH 03/16] =?UTF-8?q?feat:=20=ED=8A=B9=EC=A0=95=20=EC=97=B0?= =?UTF-8?q?=EB=8F=84=EC=97=90=20=EB=8C=80=ED=95=9C=20=EB=82=A0=EC=A7=9C?= =?UTF-8?q?=EB=B3=84=20=EC=9D=BC=EA=B8=B0=20=EC=9E=91=EC=84=B1=20=ED=86=B5?= =?UTF-8?q?=EA=B3=84=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 특정 연도에 대한 날짜별 일기 작성 통계 API 구현 --- BE/src/stat/dto/stat.tags.dto.ts | 11 ++++++ BE/src/stat/stat.controller.ts | 10 +++++- BE/src/stat/stat.service.ts | 62 +++++++++++++++++++++++++++++++- 3 files changed, 81 insertions(+), 2 deletions(-) diff --git a/BE/src/stat/dto/stat.tags.dto.ts b/BE/src/stat/dto/stat.tags.dto.ts index fb16379..2b45362 100644 --- a/BE/src/stat/dto/stat.tags.dto.ts +++ b/BE/src/stat/dto/stat.tags.dto.ts @@ -1,9 +1,20 @@ +import { sentimentStatus } from "src/utils/enum"; + export class TagInfoDto { id: number; count: number; tag: string; } +export class DiariesInfoDto { + sentiment: sentimentStatus; + date: Date; +} + export class StatTagDto { [key: string]: ({ rank: number } & TagInfoDto) | {}; } + +export class DiariesDateDto { + [dateString: string]: { sentiment: sentimentStatus; count: Number }; +} diff --git a/BE/src/stat/stat.controller.ts b/BE/src/stat/stat.controller.ts index 6853d1b..bd85788 100644 --- a/BE/src/stat/stat.controller.ts +++ b/BE/src/stat/stat.controller.ts @@ -9,7 +9,7 @@ import { GetUser } from "src/auth/get-user.decorator"; import { JwtAuthGuard } from "src/auth/guard/auth.jwt-guard"; import { User } from "src/auth/users.entity"; import { StatService } from "./stat.service"; -import { StatTagDto } from "./dto/stat.tags.dto"; +import { DiariesDateDto, StatTagDto } from "./dto/stat.tags.dto"; @Controller("stat") @UseGuards(JwtAuthGuard) @@ -23,4 +23,12 @@ export class StatController { ): Promise { return this.statService.getTopThreeTagsByUser(year, user.id); } + + @Get("/diaries/:year") + async getDiariesDate( + @Param("year", ParseIntPipe) year: number, + @GetUser() user: User, + ): Promise { + return this.statService.getDiariesDateByUser(year, user.id); + } } diff --git a/BE/src/stat/stat.service.ts b/BE/src/stat/stat.service.ts index d571559..a7a69bf 100644 --- a/BE/src/stat/stat.service.ts +++ b/BE/src/stat/stat.service.ts @@ -1,6 +1,11 @@ import { Injectable } from "@nestjs/common"; import { Diary } from "src/diaries/diaries.entity"; -import { StatTagDto, TagInfoDto } from "./dto/stat.tags.dto"; +import { + DiariesDateDto, + DiariesInfoDto, + StatTagDto, + TagInfoDto, +} from "./dto/stat.tags.dto"; @Injectable() export class StatService { @@ -15,6 +20,29 @@ export class StatService { return this.getFormatResult(result); } + async getDiariesDateByUser( + year: number, + userId: number, + ): Promise { + const diariesData = await this.fetchDiariesDateByUser(year, userId); + const formattedResult = {}; + + await diariesData.forEach((diary) => { + const { date, sentiment } = diary; + const formattedDate = this.getFormattedDate(date); + if (!formattedResult[formattedDate]) { + formattedResult[formattedDate] = { + sentiment: sentiment, + count: 1, + }; + } else { + formattedResult[formattedDate].count += 1; + } + }); + + return formattedResult; + } + private async fetchTopThreeTagsByUser( year: number, userId: number, @@ -33,6 +61,21 @@ export class StatService { .getRawMany(); } + private async fetchDiariesDateByUser( + year: number, + userId: number, + ): Promise { + return await Diary.createQueryBuilder("diary") + .select(["diary.date", "diary.updatedDate", "diary.sentiment"]) + .where("diary.user = :userId", { userId }) + .andWhere("YEAR(diary.date) = :year", { year }) + .orderBy({ + "diary.date": "ASC", + "diary.updatedDate": "DESC", + }) + .getMany(); + } + private getFormatResult(result: TagInfoDto[]): StatTagDto { const keys = ["first", "second", "third"]; const formattedResult = {}; @@ -43,4 +86,21 @@ export class StatService { return formattedResult; } + + private getFormattedDate(date: Date): string { + date.setHours(date.getHours() + 9); + let formattedDate; + formattedDate = date.getFullYear().toString() + "-"; + formattedDate += + date.getMonth() + 1 < 10 + ? "0" + (date.getMonth() + 1).toString() + : (date.getMonth() + 1).toString(); + formattedDate += "-"; + formattedDate += + date.getDate() < 10 + ? "0" + date.getDate().toString() + : date.getDate().toString(); + + return formattedDate; + } } From e2de816f8ed400a796ebb072cfe42674071b88cc Mon Sep 17 00:00:00 2001 From: JoonSoo-Kim Date: Sun, 3 Dec 2023 21:51:03 +0900 Subject: [PATCH 04/16] =?UTF-8?q?test:=20=EB=82=A0=EC=A7=9C=EB=B3=84=20?= =?UTF-8?q?=EC=9D=BC=EA=B8=B0=20=EC=9E=91=EC=84=B1=20=ED=86=B5=EA=B3=84=20?= =?UTF-8?q?API=20e2e=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 날짜별 일기 작성 통계 API e2e 테스트 작성 --- BE/test/e2e/stat.diaries.e2e-spec.ts | 89 ++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 BE/test/e2e/stat.diaries.e2e-spec.ts diff --git a/BE/test/e2e/stat.diaries.e2e-spec.ts b/BE/test/e2e/stat.diaries.e2e-spec.ts new file mode 100644 index 0000000..a47ff14 --- /dev/null +++ b/BE/test/e2e/stat.diaries.e2e-spec.ts @@ -0,0 +1,89 @@ +import { Test, TestingModule } from "@nestjs/testing"; +import { INestApplication } from "@nestjs/common"; +import * as request from "supertest"; +import { ValidationPipe } from "@nestjs/common"; +import { typeORMTestConfig } from "src/configs/typeorm.test.config"; +import { TypeOrmModule } from "@nestjs/typeorm"; +import { RedisModule } from "@liaoliaots/nestjs-redis"; +import { AuthModule } from "src/auth/auth.module"; +import { StatModule } from "src/stat/stat.module"; +import { DiariesModule } from "src/diaries/diaries.module"; + +describe("[연도별, 날짜별 일기 작성 조회] /stat/diaries/:year GET e2e 테스트", () => { + let app: INestApplication; + let accessToken: string; + + beforeAll(async () => { + const moduleFixture: TestingModule = await Test.createTestingModule({ + imports: [ + TypeOrmModule.forRoot(typeORMTestConfig), + RedisModule.forRoot({ + readyLog: true, + config: { + host: "223.130.129.145", + port: 6379, + }, + }), + StatModule, + AuthModule, + DiariesModule, + ], + }).compile(); + + app = moduleFixture.createNestApplication(); + app.enableCors(); + app.useGlobalPipes(new ValidationPipe()); + + await app.init(); + + const signInPost = await request(app.getHttpServer()) + .post("/auth/signin") + .send({ + userId: "commonUser", + password: process.env.COMMON_USER_PASS, + }); + + accessToken = signInPost.body.accessToken; + + for (let i = 1; i <= 3; i++) { + await request(app.getHttpServer()) + .post("/diaries") + .set("Authorization", `Bearer ${accessToken}`) + .send({ + title: "stat test", + content: "나는 행복해.", + point: "1.5,5.5,10.55", + date: `2023-08-0${i}`, + tags: ["tagTest"], + shapeUuid: "0c99bbc6-e404-464b-a310-5bf0fa0f0fa7", + }); + } + }); + + afterAll(async () => { + await app.close(); + }); + + it("정상 요청 시 200 OK 응답", async () => { + const postResponse = await request(app.getHttpServer()) + .get("/stat/diaries/2023") + .set("Authorization", `Bearer ${accessToken}`) + .expect(200); + + expect(Object.keys(postResponse.body).includes("2023-08-01")).toEqual(true); + expect(Object.keys(postResponse.body).includes("2023-08-02")).toEqual(true); + expect(Object.keys(postResponse.body).includes("2023-08-03")).toEqual(true); + }); + + it("액세스 토큰 없이 요청 시 401 Unauthorized 응답", async () => { + const postResponse = await request(app.getHttpServer()) + .get("/stat/diaries/2023") + .expect(401); + + expect(postResponse.body).toEqual({ + error: "Unauthorized", + message: "비로그인 상태의 요청입니다.", + statusCode: 401, + }); + }); +}); From 4579b8eb39c29dda1638ab19f9f20d2fdc9be12c Mon Sep 17 00:00:00 2001 From: JoonSoo-Kim Date: Sun, 3 Dec 2023 21:51:52 +0900 Subject: [PATCH 05/16] =?UTF-8?q?test:=20StatService=20=ED=86=B5=ED=95=A9?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - StatService의 getDirariesDateByUser 메서드에 대한 통합 테스트 작성 --- BE/test/int/stat.service.int-spec.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/BE/test/int/stat.service.int-spec.ts b/BE/test/int/stat.service.int-spec.ts index a51fc79..ff4aac0 100644 --- a/BE/test/int/stat.service.int-spec.ts +++ b/BE/test/int/stat.service.int-spec.ts @@ -1,4 +1,6 @@ import { Test, TestingModule } from "@nestjs/testing"; +import { TypeOrmModule } from "@nestjs/typeorm"; +import { typeORMTestConfig } from "src/configs/typeorm.test.config"; import { Diary } from "src/diaries/diaries.entity"; import { StatService } from "src/stat/stat.service"; @@ -7,12 +9,17 @@ describe("StatService 통합 테스트", () => { beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ + imports: [TypeOrmModule.forRoot(typeORMTestConfig)], providers: [StatService], }).compile(); service = module.get(StatService); }); + afterEach(async () => { + jest.restoreAllMocks(); + }); + describe("getTopThreeTagsByUser 메서드", () => { it("메서드 정상 요청", async () => { const year = 2023; @@ -48,4 +55,17 @@ describe("StatService 통합 테스트", () => { }); }); }); + + describe("getDiariesDateByUser 메서드", () => { + it("메서드 정상 요청", async () => { + const year = 2023; + const userId = 1; + + const result = await service.getDiariesDateByUser(year, userId); + + expect(Object.keys(result).includes("2023-08-01")).toEqual(true); + expect(Object.keys(result).includes("2023-08-02")).toEqual(true); + expect(Object.keys(result).includes("2023-08-03")).toEqual(true); + }); + }); }); From f5ae0e5e22bb07e32d3aab51af69637cc8057aeb Mon Sep 17 00:00:00 2001 From: jeongmin Date: Sun, 3 Dec 2023 22:53:17 +0900 Subject: [PATCH 06/16] =?UTF-8?q?Test:=20Jest=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EB=90=9C=20Mock=20=ED=95=A8=EC=88=98?= =?UTF-8?q?=EB=93=A4=EC=9D=84=20=EC=B4=88=EA=B8=B0=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - afterEach 함수에서 await jest.clearAllMocks()를 호출 --- BE/test/int/stat.service.int-spec.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/BE/test/int/stat.service.int-spec.ts b/BE/test/int/stat.service.int-spec.ts index 8eaa153..475da66 100644 --- a/BE/test/int/stat.service.int-spec.ts +++ b/BE/test/int/stat.service.int-spec.ts @@ -13,6 +13,10 @@ describe("StatService 통합 테스트", () => { service = module.get(StatService); }); + afterEach(async () => { + await jest.clearAllMocks(); + }); + describe("getTopThreeTagsByUser 메서드", () => { it("메서드 정상 요청", async () => { const year = 2023; From ff40771996841f04fc05585ed6e1a1df82615783 Mon Sep 17 00:00:00 2001 From: dmson1218 Date: Mon, 4 Dec 2023 15:26:57 +0900 Subject: [PATCH 07/16] =?UTF-8?q?fix:=20=ED=8E=B8=EC=A7=91=20=EB=AA=A8?= =?UTF-8?q?=EB=93=9C=20=EC=A0=84=ED=99=98=20=EC=8B=9C=20=EB=AA=A8=EB=8B=AC?= =?UTF-8?q?=EC=9D=B4=20=EB=8B=AB=ED=9E=88=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 일기 생성 모드에서 편집 모드로 넘어갈 때 모달 상태 관리 로직 추가 --- FE/src/pages/StarPage.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/FE/src/pages/StarPage.js b/FE/src/pages/StarPage.js index 6cce4ff..f28aeb1 100644 --- a/FE/src/pages/StarPage.js +++ b/FE/src/pages/StarPage.js @@ -19,6 +19,7 @@ import arrow from "../assets/arrow.svg"; import paint from "../assets/paint.svg"; function StarPage() { + const setDiaryState = useSetRecoilState(diaryAtom); const [starState, setStarState] = useRecoilState(starAtom); return ( @@ -55,6 +56,13 @@ function StarPage() { })); }} rightEvent={() => { + setDiaryState((prev) => ({ + ...prev, + isCreate: false, + isRead: false, + isUpdate: false, + isDelete: false, + })); setStarState((prev) => ({ ...prev, mode: "stella", From b4a0ff721b3f913f8b626910fee5e15ee0c0d3e1 Mon Sep 17 00:00:00 2001 From: JoonSoo-Kim Date: Mon, 4 Dec 2023 15:42:53 +0900 Subject: [PATCH 08/16] =?UTF-8?q?style:=20getFormattedDate=EB=A5=BC=20?= =?UTF-8?q?=EA=B0=84=EA=B2=B0=ED=95=98=EA=B2=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - padStart를 활용하여 getFormattedDate 메서드를 간결하게 수정 --- BE/src/stat/stat.service.ts | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/BE/src/stat/stat.service.ts b/BE/src/stat/stat.service.ts index 3bbdf78..0e1d331 100644 --- a/BE/src/stat/stat.service.ts +++ b/BE/src/stat/stat.service.ts @@ -111,18 +111,9 @@ export class StatService { private getFormattedDate(date: Date): string { date.setHours(date.getHours() + 9); - let formattedDate; - formattedDate = date.getFullYear().toString() + "-"; - formattedDate += - date.getMonth() + 1 < 10 - ? "0" + (date.getMonth() + 1).toString() - : (date.getMonth() + 1).toString(); - formattedDate += "-"; - formattedDate += - date.getDate() < 10 - ? "0" + date.getDate().toString() - : date.getDate().toString(); - return formattedDate; + return `${date.getFullYear()}-${(date.getMonth() + 1) + .toString() + .padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`; } } From 2058d4c2db5ec6d12dd6b1461103e33ab16c97ea Mon Sep 17 00:00:00 2001 From: dmson1218 Date: Mon, 4 Dec 2023 15:43:09 +0900 Subject: [PATCH 09/16] =?UTF-8?q?chore:=20=EC=9D=BC=EA=B8=B0=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20=EB=AA=A8=EB=8B=AC=20=EB=A1=9C=EB=94=A9=20/=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20UI=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FE/src/components/DiaryModal/DiaryReadModal.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/FE/src/components/DiaryModal/DiaryReadModal.js b/FE/src/components/DiaryModal/DiaryReadModal.js index 097ec37..2df56d3 100644 --- a/FE/src/components/DiaryModal/DiaryReadModal.js +++ b/FE/src/components/DiaryModal/DiaryReadModal.js @@ -106,17 +106,16 @@ function DiaryReadModal(props) { }, ); - // TODO: 로딩, 에러 처리 UI 구현 if (isLoading) return ( - + Loading... ); if (isError) return ( - + 에러 발생 ); From bf122a7c46edb44443b78a310d89c280d4415e51 Mon Sep 17 00:00:00 2001 From: jeongmin Date: Mon, 4 Dec 2023 17:28:14 +0900 Subject: [PATCH 10/16] =?UTF-8?q?hotfix:=20=EB=B3=84=EC=9E=90=EB=A6=AC?= =?UTF-8?q?=EC=99=80=20=EC=97=B0=EA=B2=B0=EB=90=9C=20=EC=9D=BC=EA=B8=B0=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20=EC=8B=9C=20=EC=97=90=EB=9F=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/diaries/diaries.module.ts | 3 ++- BE/src/diaries/diaries.repository.ts | 11 ++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/BE/src/diaries/diaries.module.ts b/BE/src/diaries/diaries.module.ts index 1a450d3..8a9dd4e 100644 --- a/BE/src/diaries/diaries.module.ts +++ b/BE/src/diaries/diaries.module.ts @@ -10,10 +10,11 @@ import { ShapesModule } from "src/shapes/shapes.module"; import { ShapesRepository } from "src/shapes/shapes.repository"; import { TagsRepository } from "src/tags/tags.repository"; import { HttpModule } from "@nestjs/axios"; +import { Line } from "src/lines/lines.entity"; @Module({ imports: [ - TypeOrmModule.forFeature([Diary]), + TypeOrmModule.forFeature([Diary, Line]), AuthModule, TagsModule, ShapesModule, diff --git a/BE/src/diaries/diaries.repository.ts b/BE/src/diaries/diaries.repository.ts index c127e28..468bc62 100644 --- a/BE/src/diaries/diaries.repository.ts +++ b/BE/src/diaries/diaries.repository.ts @@ -1,4 +1,5 @@ import { User } from "src/auth/users.entity"; +import { Line } from "src/lines/lines.entity"; import { CreateDiaryDto, DeleteDiaryDto, @@ -98,7 +99,15 @@ export class DiariesRepository { async deleteDiary(deleteDiaryDto: DeleteDiaryDto): Promise { const { uuid } = deleteDiaryDto; - const diary = await this.getDiaryByUuid(uuid); + const diary: Diary = await this.getDiaryByUuid(uuid); + + const lines = await Line.find({ + where: [ + { firstDiary: { id: diary.id } }, + { secondDiary: { id: diary.id } }, + ], + }); + await Line.softRemove(lines); await Diary.softRemove(diary); } From fae93abaa1df94cab1a8035ebc93d62260e28093 Mon Sep 17 00:00:00 2001 From: JoonSoo-Kim Date: Mon, 4 Dec 2023 21:02:30 +0900 Subject: [PATCH 11/16] =?UTF-8?q?style:=20strategies=20=EB=94=94=EB=A0=89?= =?UTF-8?q?=ED=86=A0=EB=A6=AC=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - strategies 디렉토리 생성 후 strategy 파일을 배치 --- BE/src/auth/{ => strategies}/jwt.strategy.ts | 1 + 1 file changed, 1 insertion(+) rename BE/src/auth/{ => strategies}/jwt.strategy.ts (98%) diff --git a/BE/src/auth/jwt.strategy.ts b/BE/src/auth/strategies/jwt.strategy.ts similarity index 98% rename from BE/src/auth/jwt.strategy.ts rename to BE/src/auth/strategies/jwt.strategy.ts index f72c603..a779070 100644 --- a/BE/src/auth/jwt.strategy.ts +++ b/BE/src/auth/strategies/jwt.strategy.ts @@ -24,6 +24,7 @@ export class JwtStrategy extends PassportStrategy(Strategy) { } async validate(payload: any): Promise { + console.log("jwtStrategy"); if (!payload) { throw new UnauthorizedException(); } From efa81b3c783eb3edbbd16c47b6c9c13562f5e48e Mon Sep 17 00:00:00 2001 From: JoonSoo-Kim Date: Mon, 4 Dec 2023 21:02:51 +0900 Subject: [PATCH 12/16] =?UTF-8?q?chore:=20passport-naver-v2=20=EC=84=A4?= =?UTF-8?q?=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - passport-naver-v2 설치 --- BE/package-lock.json | 46 ++++++++++++++++++++++++++++++++++++++++++++ BE/package.json | 1 + 2 files changed, 47 insertions(+) diff --git a/BE/package-lock.json b/BE/package-lock.json index 23c7f7e..53d1a7e 100644 --- a/BE/package-lock.json +++ b/BE/package-lock.json @@ -31,6 +31,7 @@ "mysql2": "^3.6.3", "passport": "^0.6.0", "passport-jwt": "^4.0.1", + "passport-naver-v2": "^2.0.8", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", "typeorm": "^0.3.17" @@ -3047,6 +3048,14 @@ } ] }, + "node_modules/base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/bcryptjs": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", @@ -7211,6 +7220,11 @@ "node": ">=8" } }, + "node_modules/oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -7465,6 +7479,33 @@ "passport-strategy": "^1.0.0" } }, + "node_modules/passport-naver-v2": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/passport-naver-v2/-/passport-naver-v2-2.0.8.tgz", + "integrity": "sha512-CA0u+aA4K4Zf5e3dSd47agOS69ULOdBGei7CZY2BN1cEbLnhnc6OalFPvnXLuEKT8I4IuGwvh3EBZCST2FoI+A==", + "dependencies": { + "passport-oauth2": "^1.5.0" + } + }, + "node_modules/passport-oauth2": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.7.0.tgz", + "integrity": "sha512-j2gf34szdTF2Onw3+76alNnaAExlUmHvkc7cL+cmaS5NzHzDP/BvFHJruueQ9XAeNOdpI+CH+PWid8RA7KCwAQ==", + "dependencies": { + "base64url": "3.x.x", + "oauth": "0.9.x", + "passport-strategy": "1.x.x", + "uid2": "0.0.x", + "utils-merge": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, "node_modules/passport-strategy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", @@ -9408,6 +9449,11 @@ "node": ">=8" } }, + "node_modules/uid2": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", + "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==" + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", diff --git a/BE/package.json b/BE/package.json index 4c40eb7..abc09fe 100644 --- a/BE/package.json +++ b/BE/package.json @@ -43,6 +43,7 @@ "mysql2": "^3.6.3", "passport": "^0.6.0", "passport-jwt": "^4.0.1", + "passport-naver-v2": "^2.0.8", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", "typeorm": "^0.3.17" From c1b12c8503311cf6f6251938156974312c6416dc Mon Sep 17 00:00:00 2001 From: JoonSoo-Kim Date: Mon, 4 Dec 2023 21:36:15 +0900 Subject: [PATCH 13/16] =?UTF-8?q?feat:=20=EB=84=A4=EC=9D=B4=EB=B2=84=20OAu?= =?UTF-8?q?th=202.0=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 네이버 인증과 정보 가공을 담당하는 NaverStrategy 작성 - 네이버 로그인 콜백을 담당하는 /auth/naver/callback API 구현 - 네이버 아이디가 들어갈 수 있도록 userId의 길이를 20에서 60으로 변경 --- BE/src/auth/auth.controller.ts | 14 ++++++++- BE/src/auth/auth.module.ts | 4 ++- BE/src/auth/auth.service.ts | 25 ++++++++++++++++ BE/src/auth/strategies/naver.strategy.ts | 38 ++++++++++++++++++++++++ BE/src/auth/users.entity.ts | 2 +- 5 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 BE/src/auth/strategies/naver.strategy.ts diff --git a/BE/src/auth/auth.controller.ts b/BE/src/auth/auth.controller.ts index 7aa0041..dd0e5b3 100644 --- a/BE/src/auth/auth.controller.ts +++ b/BE/src/auth/auth.controller.ts @@ -1,10 +1,12 @@ import { Body, Controller, + Get, HttpCode, Post, Req, UseGuards, + Redirect, } from "@nestjs/common"; import { AuthService } from "./auth.service"; import { AuthCredentialsDto } from "./dto/auth-credential.dto"; @@ -16,13 +18,23 @@ import { import { CreateUserDto } from "./dto/users.dto"; import { User } from "./users.entity"; import { GetUser } from "./get-user.decorator"; -import { JwtAuthGuard } from "./guard/auth.jwt-guard"; import { Request } from "express"; +import { AuthGuard } from "@nestjs/passport"; @Controller("auth") export class AuthController { constructor(private authService: AuthService) {} + @Get("/naver/callback") + @UseGuards(AuthGuard("naver")) + @HttpCode(201) + async naverSignIn( + @GetUser() user: User, + @Req() request: Request, + ): Promise { + return await this.authService.naverSignIn(user, request); + } + @Post("/signup") @HttpCode(204) async signUp(@Body() createUserDto: CreateUserDto): Promise { diff --git a/BE/src/auth/auth.module.ts b/BE/src/auth/auth.module.ts index a0a5d54..6b509a7 100644 --- a/BE/src/auth/auth.module.ts +++ b/BE/src/auth/auth.module.ts @@ -4,12 +4,13 @@ import { AuthController } from "./auth.controller"; import { AuthService } from "./auth.service"; import { JwtModule } from "@nestjs/jwt"; import { PassportModule } from "@nestjs/passport"; -import { JwtStrategy } from "./jwt.strategy"; +import { JwtStrategy } from "./strategies/jwt.strategy"; import { PrivateDiaryGuard } from "./guard/auth.diary-guard"; import { TypeOrmModule } from "@nestjs/typeorm"; import { User } from "./users.entity"; import { UsersRepository } from "./users.repository"; import { DiariesRepository } from "src/diaries/diaries.repository"; +import { NaverOAuthStrategy } from "./strategies/naver.strategy"; @Module({ imports: [ @@ -29,6 +30,7 @@ import { DiariesRepository } from "src/diaries/diaries.repository"; UsersRepository, PrivateDiaryGuard, DiariesRepository, + NaverOAuthStrategy, ], exports: [PassportModule, UsersRepository], }) diff --git a/BE/src/auth/auth.service.ts b/BE/src/auth/auth.service.ts index e5b20e5..ca6ec9d 100644 --- a/BE/src/auth/auth.service.ts +++ b/BE/src/auth/auth.service.ts @@ -87,4 +87,29 @@ export class AuthService { return new AccessTokenDto(accessToken); } + + async naverSignIn(user: User, request: Request): Promise { + if (!(await User.findOne({ where: { userId: user.userId } }))) { + await user.save(); + } + const userId = user.userId; + + const accessTokenPayload = { userId }; + const accessToken = await this.jwtService.sign(accessTokenPayload, { + expiresIn: "1h", + }); + + const refreshTokenPayload = { + requestIp: request.ip, + accessToken: accessToken, + }; + const refreshToken = await this.jwtService.sign(refreshTokenPayload, { + expiresIn: "24h", + }); + + // 86000s = 24h + await this.redisClient.set(userId, refreshToken, "EX", 86400); + + return new AccessTokenDto(accessToken); + } } diff --git a/BE/src/auth/strategies/naver.strategy.ts b/BE/src/auth/strategies/naver.strategy.ts new file mode 100644 index 0000000..6317696 --- /dev/null +++ b/BE/src/auth/strategies/naver.strategy.ts @@ -0,0 +1,38 @@ +import { Injectable, BadRequestException } from "@nestjs/common"; +import { PassportStrategy } from "@nestjs/passport"; +import { Request } from "express"; +import { Profile, Strategy } from "passport-naver-v2"; +import { User } from "../users.entity"; + +@Injectable() +export class NaverOAuthStrategy extends PassportStrategy(Strategy, "naver") { + constructor() { + super({ + clientID: process.env.NAVER_CLIENT_ID, + clientSecret: process.env.NAVER_CLIENT_PASS, + callbackURL: "http://localhost:3005/auth/naver/callback", + passReqToCallback: true, + }); + } + + async validate( + req: Request, + accessToken: string, + refreshToken: string, + profile: Profile, + ): Promise { + try { + const { email, id, nickname } = profile; + const user = new User(); + + user.email = email + "*naver"; + user.userId = id + "*naver"; + user.nickname = nickname; + user.password = "naver"; + + return user; + } catch (error) { + throw new BadRequestException("네이버 로그인 중 오류가 발생했습니다."); + } + } +} diff --git a/BE/src/auth/users.entity.ts b/BE/src/auth/users.entity.ts index fe6dcf0..db4e142 100644 --- a/BE/src/auth/users.entity.ts +++ b/BE/src/auth/users.entity.ts @@ -20,7 +20,7 @@ export class User extends BaseEntity { @PrimaryGeneratedColumn() id: number; - @Column({ length: 20 }) + @Column({ length: 60 }) userId: string; @Column() From 370c21e1b968f4790660437838e4bc12fbadf75f Mon Sep 17 00:00:00 2001 From: JoonSoo-Kim Date: Mon, 4 Dec 2023 22:08:46 +0900 Subject: [PATCH 14/16] =?UTF-8?q?fix:=20=EC=9D=BC=EA=B8=B0=20content?= =?UTF-8?q?=EC=9D=98=20=EA=B3=B5=EB=B0=B1=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - trim을 통해 일기의 content에서 앞 뒤 공백을 제거하여 오류 해결 --- BE/src/diaries/diaries.service.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/BE/src/diaries/diaries.service.ts b/BE/src/diaries/diaries.service.ts index 874b377..d973a64 100644 --- a/BE/src/diaries/diaries.service.ts +++ b/BE/src/diaries/diaries.service.ts @@ -28,11 +28,11 @@ export class DiariesService { async writeDiary(createDiaryDto: CreateDiaryDto, user: User): Promise { const { content, shapeUuid, tags } = createDiaryDto; + const trimContent = content.trim(); const shape = await this.shapesRepository.getShapeByUuid(shapeUuid); - const encryptedContent = await this.getEncryptedContent(content); + const encryptedContent = await this.getEncryptedContent(trimContent); const tagEntities = await this.getTags(tags); - const sentimentResult: SentimentDto = await this.getSentiment(content); - + const sentimentResult: SentimentDto = await this.getSentiment(trimContent); const diary = await this.diariesRepository.createDiary( createDiaryDto, encryptedContent, @@ -76,10 +76,11 @@ export class DiariesService { user: User, ): Promise { const { content, shapeUuid, tags } = updateDiaryDto; + const trimContent = content.trim(); const shape = await this.shapesRepository.getShapeByUuid(shapeUuid); - const encryptedContent = await this.getEncryptedContent(content); + const encryptedContent = await this.getEncryptedContent(trimContent); const tagEntities = await this.getTags(tags); - const sentimentResult = await this.getSentiment(content); + const sentimentResult = await this.getSentiment(trimContent); return this.diariesRepository.updateDiary( updateDiaryDto, From 8b3adea4ffd3365a7ca5cf6e2400ed0ee80d27a1 Mon Sep 17 00:00:00 2001 From: JoonSoo-Kim Date: Mon, 4 Dec 2023 23:22:24 +0900 Subject: [PATCH 15/16] =?UTF-8?q?feat:=20User=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - User 엔티티에 provider 칼럼을 추가 - provider 칼럼을 위한 providerEnum 작성 - provider 칼럼 추가에 따른 NaverOAuthStrategy 수정 - callbackURL을 환경변수로 관리 --- BE/src/auth/strategies/naver.strategy.ts | 6 ++++-- BE/src/auth/users.entity.ts | 6 ++++-- BE/src/utils/enum.ts | 6 ++++++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/BE/src/auth/strategies/naver.strategy.ts b/BE/src/auth/strategies/naver.strategy.ts index 6317696..1d55d26 100644 --- a/BE/src/auth/strategies/naver.strategy.ts +++ b/BE/src/auth/strategies/naver.strategy.ts @@ -3,6 +3,7 @@ import { PassportStrategy } from "@nestjs/passport"; import { Request } from "express"; import { Profile, Strategy } from "passport-naver-v2"; import { User } from "../users.entity"; +import { providerEnum } from "src/utils/enum"; @Injectable() export class NaverOAuthStrategy extends PassportStrategy(Strategy, "naver") { @@ -10,7 +11,7 @@ export class NaverOAuthStrategy extends PassportStrategy(Strategy, "naver") { super({ clientID: process.env.NAVER_CLIENT_ID, clientSecret: process.env.NAVER_CLIENT_PASS, - callbackURL: "http://localhost:3005/auth/naver/callback", + callbackURL: `${process.env.BACKEND_URL}/auth/naver/callback`, passReqToCallback: true, }); } @@ -25,10 +26,11 @@ export class NaverOAuthStrategy extends PassportStrategy(Strategy, "naver") { const { email, id, nickname } = profile; const user = new User(); - user.email = email + "*naver"; + user.email = email; user.userId = id + "*naver"; user.nickname = nickname; user.password = "naver"; + user.provider = providerEnum.NAVER; return user; } catch (error) { diff --git a/BE/src/auth/users.entity.ts b/BE/src/auth/users.entity.ts index db4e142..a24c755 100644 --- a/BE/src/auth/users.entity.ts +++ b/BE/src/auth/users.entity.ts @@ -9,13 +9,12 @@ import { OneToMany, Unique, } from "typeorm"; -import { premiumStatus } from "src/utils/enum"; +import { premiumStatus, providerEnum } from "src/utils/enum"; import { Diary } from "../diaries/diaries.entity"; import { Shape } from "src/shapes/shapes.entity"; @Entity() @Unique(["userId"]) -@Unique(["email"]) export class User extends BaseEntity { @PrimaryGeneratedColumn() id: number; @@ -47,6 +46,9 @@ export class User extends BaseEntity { @DeleteDateColumn({ type: "datetime" }) deletedDate: Date; + @Column({ type: "enum", enum: providerEnum, default: providerEnum.BYEOLSOOP }) + provider: providerEnum; + @OneToMany(() => Diary, (diary) => diary.user) diaries: Diary[]; diff --git a/BE/src/utils/enum.ts b/BE/src/utils/enum.ts index adcfd51..a9db9d3 100644 --- a/BE/src/utils/enum.ts +++ b/BE/src/utils/enum.ts @@ -9,3 +9,9 @@ export enum sentimentStatus { NEUTRAL = "neutral", ERROR = "error", } + +export enum providerEnum { + BYEOLSOOP = "byeolsoop", + NAVER = "naver", + KAKAO = "kakao", +} From 20d2bf318a20157d0a39cb2a8ce0bb234ea2a887 Mon Sep 17 00:00:00 2001 From: JoonSoo-Kim Date: Tue, 5 Dec 2023 10:30:44 +0900 Subject: [PATCH 16/16] =?UTF-8?q?style:=20=EB=A6=AC=EB=B7=B0=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BE/src/auth/auth.controller.ts | 1 - BE/src/auth/auth.service.ts | 8 ++++++-- BE/src/auth/strategies/jwt.strategy.ts | 1 - BE/src/auth/strategies/naver.strategy.ts | 7 +++---- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/BE/src/auth/auth.controller.ts b/BE/src/auth/auth.controller.ts index dd0e5b3..25a3669 100644 --- a/BE/src/auth/auth.controller.ts +++ b/BE/src/auth/auth.controller.ts @@ -6,7 +6,6 @@ import { Post, Req, UseGuards, - Redirect, } from "@nestjs/common"; import { AuthService } from "./auth.service"; import { AuthCredentialsDto } from "./dto/auth-credential.dto"; diff --git a/BE/src/auth/auth.service.ts b/BE/src/auth/auth.service.ts index ca6ec9d..c13c127 100644 --- a/BE/src/auth/auth.service.ts +++ b/BE/src/auth/auth.service.ts @@ -9,7 +9,7 @@ import { User } from "./users.entity"; import { Redis } from "ioredis"; import { InjectRedis } from "@liaoliaots/nestjs-redis"; import { Request } from "express"; -import * as jwt from "jsonwebtoken"; +import { providerEnum } from "src/utils/enum"; @Injectable() export class AuthService { @@ -89,7 +89,11 @@ export class AuthService { } async naverSignIn(user: User, request: Request): Promise { - if (!(await User.findOne({ where: { userId: user.userId } }))) { + if ( + !(await User.findOne({ + where: { userId: user.userId, provider: providerEnum.NAVER }, + })) + ) { await user.save(); } const userId = user.userId; diff --git a/BE/src/auth/strategies/jwt.strategy.ts b/BE/src/auth/strategies/jwt.strategy.ts index a779070..f72c603 100644 --- a/BE/src/auth/strategies/jwt.strategy.ts +++ b/BE/src/auth/strategies/jwt.strategy.ts @@ -24,7 +24,6 @@ export class JwtStrategy extends PassportStrategy(Strategy) { } async validate(payload: any): Promise { - console.log("jwtStrategy"); if (!payload) { throw new UnauthorizedException(); } diff --git a/BE/src/auth/strategies/naver.strategy.ts b/BE/src/auth/strategies/naver.strategy.ts index 1d55d26..214a370 100644 --- a/BE/src/auth/strategies/naver.strategy.ts +++ b/BE/src/auth/strategies/naver.strategy.ts @@ -1,6 +1,5 @@ import { Injectable, BadRequestException } from "@nestjs/common"; import { PassportStrategy } from "@nestjs/passport"; -import { Request } from "express"; import { Profile, Strategy } from "passport-naver-v2"; import { User } from "../users.entity"; import { providerEnum } from "src/utils/enum"; @@ -12,12 +11,10 @@ export class NaverOAuthStrategy extends PassportStrategy(Strategy, "naver") { clientID: process.env.NAVER_CLIENT_ID, clientSecret: process.env.NAVER_CLIENT_PASS, callbackURL: `${process.env.BACKEND_URL}/auth/naver/callback`, - passReqToCallback: true, }); } async validate( - req: Request, accessToken: string, refreshToken: string, profile: Profile, @@ -34,7 +31,9 @@ export class NaverOAuthStrategy extends PassportStrategy(Strategy, "naver") { return user; } catch (error) { - throw new BadRequestException("네이버 로그인 중 오류가 발생했습니다."); + throw new BadRequestException( + `네이버 로그인 중 오류가 발생했습니다 : ${error.message}`, + ); } } }