From b3b866913a477dcb5635f7eb8490b8fbfb68efba Mon Sep 17 00:00:00 2001 From: jeongmin Date: Mon, 20 Nov 2023 17:55:40 +0900 Subject: [PATCH 01/24] =?UTF-8?q?Feat:=20DB=EC=9D=98=20shape=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=EC=97=90=20=EA=B8=B0=EB=B3=B8=20=EC=A0=9C?= =?UTF-8?q?=EA=B3=B5=20=EB=AA=A8=EC=96=91=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AppModule에서 commonUser 생성 후 User 정보로 추가 - 기본 모양 데이터를 Shape 테이블에 추가 - 이미 유저가 있거나, 모양 데이터 파일이 있다면 생성하지 않음 --- BE/src/app.module.ts | 24 ++++++++++++++++++++- BE/src/shapes/shapes.default.ts | 7 ++++++ BE/src/shapes/shapes.module.ts | 15 +++++++++++++ BE/src/shapes/shapes.repository.ts | 34 ++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 BE/src/shapes/shapes.default.ts create mode 100644 BE/src/shapes/shapes.module.ts create mode 100644 BE/src/shapes/shapes.repository.ts diff --git a/BE/src/app.module.ts b/BE/src/app.module.ts index c26449b..8f659dd 100644 --- a/BE/src/app.module.ts +++ b/BE/src/app.module.ts @@ -5,6 +5,9 @@ import { UsersModule } from "./users/users.module"; import { DiariesModule } from "./diaries/diaries.module"; import { AuthModule } from "./auth/auth.module"; import { IntroduceModule } from "./introduce/introduce.module"; +import { ShapesModule } from "./shapes/shapes.module"; +import { ShapesRepository } from "./shapes/shapes.repository"; +import { UsersRepository } from "./users/users.repository"; @Module({ imports: [ @@ -13,6 +16,25 @@ import { IntroduceModule } from "./introduce/introduce.module"; DiariesModule, AuthModule, IntroduceModule, + ShapesModule, ], + providers: [ShapesRepository, UsersRepository], }) -export class AppModule {} +export class AppModule { + constructor( + private shapesRepository: ShapesRepository, + private usersRepository: UsersRepository, + ) {} + + async onModuleInit() { + const commonUser = + (await this.usersRepository.getUserByUserId("commonUser")) || + (await this.usersRepository.createUser({ + userId: "commonUser", + password: process.env.COMMON_USER_PASS, + nickname: "commonUser", + })); + + await this.shapesRepository.createDefaultShapes(commonUser); + } +} diff --git a/BE/src/shapes/shapes.default.ts b/BE/src/shapes/shapes.default.ts new file mode 100644 index 0000000..e2101ea --- /dev/null +++ b/BE/src/shapes/shapes.default.ts @@ -0,0 +1,7 @@ +import { Shape } from "./shapes.entity"; + +export const defaultShapes: Partial[] = [ + { shapePath: "test-basic-shape-1.png" }, + { shapePath: "test-basic-shape-2.png" }, + { shapePath: "test-basic-shape-3.png" }, +]; diff --git a/BE/src/shapes/shapes.module.ts b/BE/src/shapes/shapes.module.ts new file mode 100644 index 0000000..34c5591 --- /dev/null +++ b/BE/src/shapes/shapes.module.ts @@ -0,0 +1,15 @@ +import { Module } from "@nestjs/common"; +import { TypeOrmModule } from "@nestjs/typeorm"; +import { UsersModule } from "src/users/users.module"; +import { Shape } from "./shapes.entity"; +import { ShapesController } from "./shapes.controller"; +import { ShapesService } from "./shapes.service"; +import { ShapesRepository } from "./shapes.repository"; +import { AuthModule } from "src/auth/auth.module"; + +@Module({ + imports: [TypeOrmModule.forFeature([Shape]), UsersModule, AuthModule], + controllers: [ShapesController], + providers: [ShapesService, ShapesRepository], +}) +export class ShapesModule {} diff --git a/BE/src/shapes/shapes.repository.ts b/BE/src/shapes/shapes.repository.ts new file mode 100644 index 0000000..7fa47b0 --- /dev/null +++ b/BE/src/shapes/shapes.repository.ts @@ -0,0 +1,34 @@ +import { defaultShapes } from "./shapes.default"; +import { User } from "src/users/users.entity"; +import { Shape } from "./shapes.entity"; +import { NotFoundException } from "@nestjs/common"; + +export class ShapesRepository { + async createDefaultShapes(commonUser: User) { + await Promise.all( + defaultShapes.map(async (defaultShape) => { + const existingShape = await this.getShapeByShapePath( + defaultShape.shapePath, + ); + + if (!existingShape) { + defaultShape.user = commonUser; + const shape = Shape.create(defaultShape); + await shape.save(); + } + }), + ); + } + + async getShapeByShapePath(shapePath: string): Promise { + return Shape.findOne({ where: { shapePath } }); + } + + async getShapeByUuid(uuid: string): Promise { + const found = await Shape.findOne({ where: { uuid } }); + if (!found) { + throw new NotFoundException(`Can't find Shape with uuid: [${uuid}]`); + } + return found; + } +} From 96318b90348adf9b6a1e1c50e4621f7b3c566e8e Mon Sep 17 00:00:00 2001 From: jeongmin Date: Mon, 20 Nov 2023 22:49:11 +0900 Subject: [PATCH 02/24] =?UTF-8?q?Feat:=20Shape=20=EC=84=9C=EB=B9=84?= =?UTF-8?q?=EC=8A=A4=20=EB=B0=8F=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기본 도형 파일에 대한 Shape 객체 배열을 반환하는 함수 구현 - uuid에 해당하는 ShapeFile을 반환하는 함수 구현 --- BE/src/shapes/shapes.controller.ts | 21 +++++++++++++++++++++ BE/src/shapes/shapes.service.ts | 24 ++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 BE/src/shapes/shapes.controller.ts create mode 100644 BE/src/shapes/shapes.service.ts diff --git a/BE/src/shapes/shapes.controller.ts b/BE/src/shapes/shapes.controller.ts new file mode 100644 index 0000000..0d8b4db --- /dev/null +++ b/BE/src/shapes/shapes.controller.ts @@ -0,0 +1,21 @@ +import { Controller, Get, Param, UseGuards } from "@nestjs/common"; +import { ShapesService } from "./shapes.service"; +import { AuthGuard } from "@nestjs/passport"; +import { IdGuard } from "src/auth/auth.id-guard"; +import { Shape } from "./shapes.entity"; + +@Controller("shapes") +export class ShapesController { + constructor(private shapesService: ShapesService) {} + + @Get("/default") + async getDefaultShapeFiles(): Promise { + return this.shapesService.getDefaultShapeFiles(); + } + + @Get("/:uuid") + @UseGuards(AuthGuard(), IdGuard) + async getShapeFilesByUuid(@Param("uuid") uuid: string): Promise { + return this.shapesService.getShapeFileByUuid(uuid); + } +} diff --git a/BE/src/shapes/shapes.service.ts b/BE/src/shapes/shapes.service.ts new file mode 100644 index 0000000..2912f1d --- /dev/null +++ b/BE/src/shapes/shapes.service.ts @@ -0,0 +1,24 @@ +import { Injectable } from "@nestjs/common"; +import { ShapesRepository } from "./shapes.repository"; +import { getFileFromS3 } from "src/utils/e3"; +import { defaultShapes } from "./shapes.default"; +import { Shape } from "./shapes.entity"; + +@Injectable() +export class ShapesService { + constructor(private shapesRepository: ShapesRepository) {} + + async getDefaultShapeFiles(): Promise { + const promises = defaultShapes.map(async (defaultShape) => { + return this.shapesRepository.getShapeByShapePath(defaultShape.shapePath); + }); + + const shapeFiles = await Promise.all(promises); + return shapeFiles; + } + + async getShapeFileByUuid(uuid: string): Promise { + const shape = await this.shapesRepository.getShapeByUuid(uuid); + return getFileFromS3(shape.shapePath); + } +} From 25a2e1cd5df3b9244827266f4a6bb889d5826feb Mon Sep 17 00:00:00 2001 From: jeongmin Date: Mon, 20 Nov 2023 23:33:34 +0900 Subject: [PATCH 03/24] =?UTF-8?q?Feat:=20GetUser=20=EB=8D=B0=EC=BD=94?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=ED=84=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 요청으로부터 사용자 정보를 가져오는 GetUser 데코레이터 추가 --- BE/src/auth/get-user.decorator.ts | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 BE/src/auth/get-user.decorator.ts diff --git a/BE/src/auth/get-user.decorator.ts b/BE/src/auth/get-user.decorator.ts new file mode 100644 index 0000000..e32f885 --- /dev/null +++ b/BE/src/auth/get-user.decorator.ts @@ -0,0 +1,9 @@ +import { createParamDecorator, ExecutionContext } from "@nestjs/common"; +import { User } from "src/users/users.entity"; + +export const GetUser = createParamDecorator( + (_: unknown, ctx: ExecutionContext): User => { + const req = ctx.switchToHttp().getRequest(); + return req.user; + }, +); From a34ef6e0fc4cb4945ec242f09262da7ffdd49c31 Mon Sep 17 00:00:00 2001 From: jeongmin Date: Mon, 20 Nov 2023 23:36:23 +0900 Subject: [PATCH 04/24] =?UTF-8?q?Fix:=20/shapes/:uuid=20=EC=9A=94=EC=B2=AD?= =?UTF-8?q?=EC=97=90=20=EB=8C=80=ED=95=9C=20=EC=9C=A0=EC=A0=80=20=EA=B6=8C?= =?UTF-8?q?=ED=95=9C=20=ED=99=95=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 해당하는 유저의 데이터인지 확인하는 조건문 추가 - @GetUser 데코레이터 활용 - Shape Entity의 user 컬럼을 Lazy relations으로 설정 --- BE/src/shapes/shapes.controller.ts | 12 ++++++++---- BE/src/shapes/shapes.entity.ts | 2 +- BE/src/shapes/shapes.repository.ts | 2 +- BE/src/shapes/shapes.service.ts | 9 +++++++-- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/BE/src/shapes/shapes.controller.ts b/BE/src/shapes/shapes.controller.ts index 0d8b4db..856a643 100644 --- a/BE/src/shapes/shapes.controller.ts +++ b/BE/src/shapes/shapes.controller.ts @@ -1,8 +1,9 @@ import { Controller, Get, Param, UseGuards } from "@nestjs/common"; import { ShapesService } from "./shapes.service"; import { AuthGuard } from "@nestjs/passport"; -import { IdGuard } from "src/auth/auth.id-guard"; import { Shape } from "./shapes.entity"; +import { GetUser } from "src/auth/get-user.decorator"; +import { User } from "src/users/users.entity"; @Controller("shapes") export class ShapesController { @@ -14,8 +15,11 @@ export class ShapesController { } @Get("/:uuid") - @UseGuards(AuthGuard(), IdGuard) - async getShapeFilesByUuid(@Param("uuid") uuid: string): Promise { - return this.shapesService.getShapeFileByUuid(uuid); + @UseGuards(AuthGuard()) + async getShapeFilesByUuid( + @Param("uuid") uuid: string, + @GetUser() user: User, + ): Promise { + return this.shapesService.getShapeFileByUuid(uuid, user); } } diff --git a/BE/src/shapes/shapes.entity.ts b/BE/src/shapes/shapes.entity.ts index 97408eb..4898dbe 100644 --- a/BE/src/shapes/shapes.entity.ts +++ b/BE/src/shapes/shapes.entity.ts @@ -20,7 +20,7 @@ export class Shape extends BaseEntity { uuid: string; @ManyToOne(() => User, (user) => user.userId, { nullable: false }) - user: User; + user: Promise; @Column() shapePath: string; diff --git a/BE/src/shapes/shapes.repository.ts b/BE/src/shapes/shapes.repository.ts index 7fa47b0..1ce95d9 100644 --- a/BE/src/shapes/shapes.repository.ts +++ b/BE/src/shapes/shapes.repository.ts @@ -12,7 +12,7 @@ export class ShapesRepository { ); if (!existingShape) { - defaultShape.user = commonUser; + defaultShape.user = Promise.resolve(commonUser); const shape = Shape.create(defaultShape); await shape.save(); } diff --git a/BE/src/shapes/shapes.service.ts b/BE/src/shapes/shapes.service.ts index 2912f1d..a58570b 100644 --- a/BE/src/shapes/shapes.service.ts +++ b/BE/src/shapes/shapes.service.ts @@ -1,8 +1,9 @@ -import { Injectable } from "@nestjs/common"; +import { Injectable, UnauthorizedException } from "@nestjs/common"; import { ShapesRepository } from "./shapes.repository"; import { getFileFromS3 } from "src/utils/e3"; import { defaultShapes } from "./shapes.default"; import { Shape } from "./shapes.entity"; +import { User } from "src/users/users.entity"; @Injectable() export class ShapesService { @@ -17,8 +18,12 @@ export class ShapesService { return shapeFiles; } - async getShapeFileByUuid(uuid: string): Promise { + async getShapeFileByUuid(uuid: string, user: User): Promise { const shape = await this.shapesRepository.getShapeByUuid(uuid); + + if ((await shape.user).id !== user.id) { + throw new UnauthorizedException(); + } return getFileFromS3(shape.shapePath); } } From 4d9a0da686830051d1b7896372064ac0e291c485 Mon Sep 17 00:00:00 2001 From: jeongmin Date: Tue, 21 Nov 2023 00:03:18 +0900 Subject: [PATCH 05/24] =?UTF-8?q?Refactor:=20getShapeFileByUuid=20?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=EC=9D=98=20=EA=B8=B0=EB=B3=B8=20=EB=AA=A8?= =?UTF-8?q?=EC=96=91=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 로그인한 상태로 기본 모양에 대한 uuid를 통해 getShapeFileByUuid 함수 호출 시 유저 상관없이 반환 --- BE/src/shapes/shapes.service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/BE/src/shapes/shapes.service.ts b/BE/src/shapes/shapes.service.ts index a58570b..552b41a 100644 --- a/BE/src/shapes/shapes.service.ts +++ b/BE/src/shapes/shapes.service.ts @@ -20,8 +20,9 @@ export class ShapesService { async getShapeFileByUuid(uuid: string, user: User): Promise { const shape = await this.shapesRepository.getShapeByUuid(uuid); + const shapeUser = await shape.user; - if ((await shape.user).id !== user.id) { + if (shapeUser.userId !== "commonUser" && shapeUser.id !== user.id) { throw new UnauthorizedException(); } return getFileFromS3(shape.shapePath); From ad5c5ba69937dd35af86b75192a76294b508ad3f Mon Sep 17 00:00:00 2001 From: dbwhdtjr0457 Date: Tue, 21 Nov 2023 14:01:26 +0900 Subject: [PATCH 06/24] =?UTF-8?q?fix:=20=EB=AA=A8=EB=8B=AC=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=ED=8F=B0=ED=8A=B8=20pretendard=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FE/src/styles/Modal/ModalButton.js | 1 + 1 file changed, 1 insertion(+) diff --git a/FE/src/styles/Modal/ModalButton.js b/FE/src/styles/Modal/ModalButton.js index 1a92526..6039d6a 100644 --- a/FE/src/styles/Modal/ModalButton.js +++ b/FE/src/styles/Modal/ModalButton.js @@ -12,6 +12,7 @@ const ModalButton = styled.button` align-items: center; gap: 1rem; + font-family: "pretendard-medium"; font-size: ${(props) => props.fontSize || "1.25rem"}; color: ${(props) => props.color || "#ffffff"}; From 40bde26f59becc6d4ffff39814a536290caaafef Mon Sep 17 00:00:00 2001 From: jeongmin Date: Tue, 21 Nov 2023 14:26:07 +0900 Subject: [PATCH 07/24] =?UTF-8?q?Refactor:=20crong=EB=8B=98=20=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 해체할당 문법 사용 - 조건문 수정 후 들여쓰기 수정 --- BE/src/shapes/shapes.repository.ts | 10 +++++----- BE/src/shapes/shapes.service.ts | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/BE/src/shapes/shapes.repository.ts b/BE/src/shapes/shapes.repository.ts index 1ce95d9..95c72e9 100644 --- a/BE/src/shapes/shapes.repository.ts +++ b/BE/src/shapes/shapes.repository.ts @@ -11,11 +11,11 @@ export class ShapesRepository { defaultShape.shapePath, ); - if (!existingShape) { - defaultShape.user = Promise.resolve(commonUser); - const shape = Shape.create(defaultShape); - await shape.save(); - } + if (existingShape) return; + + defaultShape.user = Promise.resolve(commonUser); + const shape = Shape.create(defaultShape); + await shape.save(); }), ); } diff --git a/BE/src/shapes/shapes.service.ts b/BE/src/shapes/shapes.service.ts index 552b41a..306ab32 100644 --- a/BE/src/shapes/shapes.service.ts +++ b/BE/src/shapes/shapes.service.ts @@ -20,9 +20,9 @@ export class ShapesService { async getShapeFileByUuid(uuid: string, user: User): Promise { const shape = await this.shapesRepository.getShapeByUuid(uuid); - const shapeUser = await shape.user; + const { userId, id } = await shape.user; - if (shapeUser.userId !== "commonUser" && shapeUser.id !== user.id) { + if (userId !== "commonUser" && id !== user.id) { throw new UnauthorizedException(); } return getFileFromS3(shape.shapePath); From cc4f0d4e8003a67c8c30397f483b32de3b410208 Mon Sep 17 00:00:00 2001 From: dbwhdtjr0457 Date: Tue, 21 Nov 2023 15:21:21 +0900 Subject: [PATCH 08/24] =?UTF-8?q?feat:=20=EB=B9=88=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20input=20=EC=8B=9C=20=EA=B2=BD=EA=B3=A0=20=EB=AC=B8?= =?UTF-8?q?=EA=B5=AC=20=EC=B6=9C=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 로그인 input이 빈 상태로 로그인 시도 시 경고 문구 출력하도록 간단히 구현하였습니다. --- FE/src/components/LoginModal/LoginModal.js | 70 +++++++++++++++------- 1 file changed, 48 insertions(+), 22 deletions(-) diff --git a/FE/src/components/LoginModal/LoginModal.js b/FE/src/components/LoginModal/LoginModal.js index 1cb1ceb..1a1670c 100644 --- a/FE/src/components/LoginModal/LoginModal.js +++ b/FE/src/components/LoginModal/LoginModal.js @@ -1,8 +1,5 @@ -import React from "react"; +import React, { useState, useRef } from "react"; import styled from "styled-components"; -import { useSetRecoilState } from "recoil"; -import headerAtom from "../../atoms/headerAtom"; -import userAtom from "../../atoms/userAtom"; import ModalWrapper from "../../styles/Modal/ModalWrapper"; import ModalTitle from "../../styles/Modal/ModalTitle"; import ModalButton from "../../styles/Modal/ModalButton"; @@ -12,35 +9,55 @@ import kakao from "../../assets/kakao.png"; import naver from "../../assets/naver.png"; function LoginModal() { - // temp: sidebar 테스트용 - const setHeaderState = useSetRecoilState(headerAtom); - const setUserState = useSetRecoilState(userAtom); + const [id, setId] = useState(""); + const [password, setPassword] = useState(""); + const errorRef = useRef(); + + function checkValid() { + if (id === "") { + errorRef.current.innerText = "아이디를 입력해주세요"; + return; + } + if (password === "") { + errorRef.current.innerText = "비밀번호를 입력해주세요"; + return; + } + if (id !== "commonUser" || password !== "SecretCommon") { + errorRef.current.innerText = "아이디 또는 비밀번호가 틀렸습니다"; + return; + } + + errorRef.current.innerText = ""; + } return ( <> 로그인 - - + setId(e.target.value)} + /> + setPassword(e.target.value)} + />
로그인 유지
- { - setHeaderState({ - isLogin: false, - isSignUp: false, - }); - setUserState({ - isLogin: true, - }); - }} - > - 로그인 - + +
+ checkValid()}> + 로그인 + +
회원가입
@@ -76,6 +93,15 @@ const InputBar = styled.div` gap: 0.5rem; `; +const ModalButtonContainer = styled.div` + display: flex; + flex-direction: column; + justify-content: space-between; + width: 100%; + height: 5rem; + margin-top: -1rem; +`; + const CheckBar = styled.div` width: 100%; margin-top: 0.5rem; From c1fa37357aa96b25fa9aa3bfc2a244c0eb7b0a34 Mon Sep 17 00:00:00 2001 From: JoonSoo-Kim Date: Tue, 21 Nov 2023 16:53:45 +0900 Subject: [PATCH 09/24] =?UTF-8?q?refactor:=20=EC=82=AC=EC=9A=A9=EC=9E=90?= =?UTF-8?q?=20=EC=97=94=ED=8B=B0=ED=8B=B0,=20DTO=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 사용자 엔티티, DTO를 API 문서에 맞게 수정 --- BE/src/users/users.dto.ts | 15 ++++++++++----- BE/src/users/users.entity.ts | 3 +++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/BE/src/users/users.dto.ts b/BE/src/users/users.dto.ts index 8cbd982..1e346f5 100644 --- a/BE/src/users/users.dto.ts +++ b/BE/src/users/users.dto.ts @@ -1,24 +1,29 @@ -import { IsString, IsNumber, IsEnum, MaxLength } from "class-validator"; -import { premiumStatus } from "src/utils/enum"; +import { IsString, Length, MaxLength, Matches } from "class-validator"; export class CreateUserDto { @IsString() - @MaxLength(20) + @Length(4, 21) userId: string; @IsString() - @MaxLength(20) + @Matches("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$") + email: string; + + @IsString() + @Length(4, 21) password: string; @IsString() - @MaxLength(20) + @MaxLength(21) nickname: string; } export class LoginUserDto { @IsString() + @Length(4, 21) userId: string; @IsString() + @Length(4, 21) password: string; } diff --git a/BE/src/users/users.entity.ts b/BE/src/users/users.entity.ts index dced87c..0cccb11 100644 --- a/BE/src/users/users.entity.ts +++ b/BE/src/users/users.entity.ts @@ -20,6 +20,9 @@ export class User extends BaseEntity { @Column({ length: 20, unique: true }) userId: string; + @Column({ unique: true }) + email: string; + @Column({ length: 60 }) password: string; From b0aa3be27193cf7270005e6e33bf87b1df66d051 Mon Sep 17 00:00:00 2001 From: JoonSoo-Kim Date: Tue, 21 Nov 2023 16:54:17 +0900 Subject: [PATCH 10/24] =?UTF-8?q?refactor:=20=EC=9D=BC=EA=B8=B0=20?= =?UTF-8?q?=EC=97=94=ED=8B=B0=ED=8B=B0,=20DTO=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 일기 엔티티, DTO를 API 문서에 맞게 수정 --- BE/src/app.module.ts | 1 + BE/src/diaries/diaries.dto.ts | 14 +++++++++++++- BE/src/diaries/diaries.entity.ts | 3 --- BE/src/diaries/diaries.repository.ts | 2 -- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/BE/src/app.module.ts b/BE/src/app.module.ts index 8f659dd..e3bb558 100644 --- a/BE/src/app.module.ts +++ b/BE/src/app.module.ts @@ -33,6 +33,7 @@ export class AppModule { userId: "commonUser", password: process.env.COMMON_USER_PASS, nickname: "commonUser", + email: "commonuser@email.com", })); await this.shapesRepository.createDefaultShapes(commonUser); diff --git a/BE/src/diaries/diaries.dto.ts b/BE/src/diaries/diaries.dto.ts index c4701ee..7a86f85 100644 --- a/BE/src/diaries/diaries.dto.ts +++ b/BE/src/diaries/diaries.dto.ts @@ -18,6 +18,9 @@ export class CreateDiaryDto { @IsArray() tags: string[]; + + @IsUUID() + shapeUuid: string; } export class ReadDiaryDto { @@ -35,10 +38,19 @@ export class UpdateDiaryDto { @IsString() content: string; + @IsString() + @Matches(RegExp("^-?d+(.d+)?,-?d+(.d+)?,-?d+(.d+)?$"), { + message: "적절하지 않은 포인트 양식입니다", + }) + point: string; + @IsDate() date: Date; - @IsString() + @IsArray() + tags: string[]; + + @IsUUID() shapeUuid: string; } diff --git a/BE/src/diaries/diaries.entity.ts b/BE/src/diaries/diaries.entity.ts index 9e22ec3..1f54bf3 100644 --- a/BE/src/diaries/diaries.entity.ts +++ b/BE/src/diaries/diaries.entity.ts @@ -44,9 +44,6 @@ export class Diary extends BaseEntity { @Column({ type: "text" }) content: string; - @Column({ length: 7 }) - color: string; - @Column({ type: "float" }) positiveRatio: number; diff --git a/BE/src/diaries/diaries.repository.ts b/BE/src/diaries/diaries.repository.ts index 82c1ca9..5079e30 100644 --- a/BE/src/diaries/diaries.repository.ts +++ b/BE/src/diaries/diaries.repository.ts @@ -21,7 +21,6 @@ export class DiariesRepository { const content = encodedContent; // 미구현 기능을 대체하기 위한 임시 값 - const color = "#FFFFFF"; const positiveRatio = 0.0; const negativeRatio = 100.0; const neutralRatio = 0.0; @@ -34,7 +33,6 @@ export class DiariesRepository { content, point, date, - color, positiveRatio, negativeRatio, neutralRatio, From 3ac646da9e3ace1360ddbbc42460a08bd3fdf4a6 Mon Sep 17 00:00:00 2001 From: JoonSoo-Kim Date: Tue, 21 Nov 2023 16:55:00 +0900 Subject: [PATCH 11/24] =?UTF-8?q?fix:=20DB=20synchronize=20false=EB=A1=9C?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DB 구조가 변화하지 않게 synchronize false로 변경 --- BE/src/configs/typeorm.config.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BE/src/configs/typeorm.config.ts b/BE/src/configs/typeorm.config.ts index 2e59cc3..9412a77 100644 --- a/BE/src/configs/typeorm.config.ts +++ b/BE/src/configs/typeorm.config.ts @@ -9,7 +9,7 @@ export const typeORMConfig: TypeOrmModuleOptions = { password: process.env.DB_PASS, database: process.env.DB_NAME, entities: ["dist/**/*.entity{.ts,.js}"], - synchronize: true, + synchronize: false, timezone: "+09:00", }; @@ -19,7 +19,7 @@ export const typeORMTestConfig: TypeOrmModuleOptions = { port: 3306, username: process.env.DB_USER, password: process.env.DB_PASS, - database: process.env.DB_NAME, + database: process.env.TEST_DB_NAME, entities: ["src/**/*.entity{.ts,.js}"], synchronize: true, timezone: "+09:00", From 50f676349455b947667f9c8e70899e46e2b29fdc Mon Sep 17 00:00:00 2001 From: JoonSoo-Kim Date: Tue, 21 Nov 2023 16:55:15 +0900 Subject: [PATCH 12/24] =?UTF-8?q?fix:=20CORS=20=EC=A0=95=EC=B1=85=20?= =?UTF-8?q?=EC=9C=84=EB=B0=98=20=EC=98=A4=EB=A5=98=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CORS 정책을 enable하여 해결 --- BE/src/main.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/BE/src/main.ts b/BE/src/main.ts index a07842e..ad40d5b 100644 --- a/BE/src/main.ts +++ b/BE/src/main.ts @@ -3,6 +3,7 @@ import { AppModule } from "./app.module"; async function bootstrap() { const app = await NestFactory.create(AppModule); + app.enableCors(); await app.listen(process.env.BE_PORT); } bootstrap(); From 8b0cfc04d784b19ed4c6469d913288ee2479887b Mon Sep 17 00:00:00 2001 From: jeongmin Date: Tue, 21 Nov 2023 16:58:36 +0900 Subject: [PATCH 13/24] =?UTF-8?q?Fix:=20getUserByUserId=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EC=B2=98=EB=A6=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 이전에는 throw 로 Error를 보냄 - 현재는 찾지 못했을 경우 Null 반환하여 다른 부분에서 처리를 하도록 수정 --- BE/src/users/users.repository.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/BE/src/users/users.repository.ts b/BE/src/users/users.repository.ts index 898f4ab..5f8d1d9 100644 --- a/BE/src/users/users.repository.ts +++ b/BE/src/users/users.repository.ts @@ -28,11 +28,7 @@ export class UsersRepository { return user; } - async getUserByUserId(userId: string): Promise { - const found = await User.findOne({ where: { userId } }); - if (!found) { - throw new NotFoundException(`Can't find User with UserId: [${userId}]`); - } - return found; + async getUserByUserId(userId: string): Promise { + return User.findOne({ where: { userId } }); } } From c6d03e001d1efbbd1550fcc64a10b6fcb0404686 Mon Sep 17 00:00:00 2001 From: JoonSoo-Kim Date: Tue, 21 Nov 2023 17:03:07 +0900 Subject: [PATCH 14/24] =?UTF-8?q?feat:=20commonUser=EC=9D=98=20=EC=9D=B4?= =?UTF-8?q?=EB=A9=94=EC=9D=BC=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - commonUser의 이메일을 실제 별숲 공동 계정의 이메일로 변경 --- BE/src/app.module.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BE/src/app.module.ts b/BE/src/app.module.ts index e3bb558..fbbc3fe 100644 --- a/BE/src/app.module.ts +++ b/BE/src/app.module.ts @@ -33,7 +33,7 @@ export class AppModule { userId: "commonUser", password: process.env.COMMON_USER_PASS, nickname: "commonUser", - email: "commonuser@email.com", + email: "byeolsoop08@naver.com", })); await this.shapesRepository.createDefaultShapes(commonUser); From 6e75ef00678c097a16d3cd93c6e7df1a05a85cc1 Mon Sep 17 00:00:00 2001 From: dbwhdtjr0457 Date: Tue, 21 Nov 2023 17:05:42 +0900 Subject: [PATCH 15/24] temp --- FE/src/components/LoginModal/LoginModal.js | 36 +++++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/FE/src/components/LoginModal/LoginModal.js b/FE/src/components/LoginModal/LoginModal.js index 1a1670c..55ddba8 100644 --- a/FE/src/components/LoginModal/LoginModal.js +++ b/FE/src/components/LoginModal/LoginModal.js @@ -1,4 +1,5 @@ -import React, { useState, useRef } from "react"; +import React, { useState, useRef, useEffect } from "react"; +import { useSetRecoilState } from "recoil"; import styled from "styled-components"; import ModalWrapper from "../../styles/Modal/ModalWrapper"; import ModalTitle from "../../styles/Modal/ModalTitle"; @@ -7,10 +8,13 @@ import ModalInputBox from "../../styles/Modal/ModalInputBox"; import ModalBackground from "../ModalBackground/ModalBackground"; import kakao from "../../assets/kakao.png"; import naver from "../../assets/naver.png"; +import userAtom from "../../atoms/userAtom"; function LoginModal() { + const setUserState = useSetRecoilState(userAtom); const [id, setId] = useState(""); const [password, setPassword] = useState(""); + const [isValid, setIsValid] = useState(false); const errorRef = useRef(); function checkValid() { @@ -22,14 +26,36 @@ function LoginModal() { errorRef.current.innerText = "비밀번호를 입력해주세요"; return; } - if (id !== "commonUser" || password !== "SecretCommon") { - errorRef.current.innerText = "아이디 또는 비밀번호가 틀렸습니다"; - return; - } errorRef.current.innerText = ""; + + setIsValid(true); } + useEffect(() => { + // id: commonUser, password: SecretCommon + if (isValid) { + fetch("http://223.130.129.145:3005/auth/signin", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ id, password }), + }) + .then((res) => res.json()) + .then((res) => { + if (res.accessToken) { + setUserState({ isLogin: true }); + } else { + errorRef.current.innerText = res.message; + } + }) + .catch((err) => { + console.log(err); + }); + } + }, [isValid]); + return ( <> From 157189f7d39c477b4f401a5eb4f1b4855b1490ad Mon Sep 17 00:00:00 2001 From: dbwhdtjr0457 Date: Tue, 21 Nov 2023 17:49:32 +0900 Subject: [PATCH 16/24] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20API?= =?UTF-8?q?=EB=A5=BC=20fetch=EB=A1=9C=20=EC=97=B0=EA=B2=B0=20=EB=B0=8F=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit id commonUser, password SecretCommon으로 로그인할 수 있도록 fetch를 이용해 구현하였습니다. --- FE/src/components/LoginModal/LoginModal.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/FE/src/components/LoginModal/LoginModal.js b/FE/src/components/LoginModal/LoginModal.js index 55ddba8..84232fc 100644 --- a/FE/src/components/LoginModal/LoginModal.js +++ b/FE/src/components/LoginModal/LoginModal.js @@ -12,13 +12,13 @@ import userAtom from "../../atoms/userAtom"; function LoginModal() { const setUserState = useSetRecoilState(userAtom); - const [id, setId] = useState(""); + const [userId, setUserId] = useState(""); const [password, setPassword] = useState(""); const [isValid, setIsValid] = useState(false); const errorRef = useRef(); function checkValid() { - if (id === "") { + if (userId === "") { errorRef.current.innerText = "아이디를 입력해주세요"; return; } @@ -40,7 +40,7 @@ function LoginModal() { headers: { "Content-Type": "application/json", }, - body: JSON.stringify({ id, password }), + body: JSON.stringify({ userId, password }), }) .then((res) => res.json()) .then((res) => { @@ -64,8 +64,8 @@ function LoginModal() { setId(e.target.value)} + value={userId} + onChange={(e) => setUserId(e.target.value)} /> Date: Tue, 21 Nov 2023 18:16:51 +0900 Subject: [PATCH 17/24] =?UTF-8?q?Fix:=20=EC=9C=A0=EC=A0=80=20DTO=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=97=90=20=EB=94=B0=EB=A5=B8=20Repository?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - createUser에서 email을 저장하지 않는 오류 수정 - email 정규표현식 오류 수정 --- BE/src/diaries/diaries.dto.ts | 2 +- BE/src/users/users.dto.ts | 4 +++- BE/src/users/users.repository.ts | 9 +++++++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/BE/src/diaries/diaries.dto.ts b/BE/src/diaries/diaries.dto.ts index 7a86f85..6cd5c32 100644 --- a/BE/src/diaries/diaries.dto.ts +++ b/BE/src/diaries/diaries.dto.ts @@ -8,7 +8,7 @@ export class CreateDiaryDto { content: string; @IsString() - @Matches(RegExp("^-?d+(.d+)?,-?d+(.d+)?,-?d+(.d+)?$"), { + @Matches(RegExp(/^-?d+(.d+)?,-?d+(.d+)?,-?d+(.d+)?$/), { message: "적절하지 않은 포인트 양식입니다", }) point: string; diff --git a/BE/src/users/users.dto.ts b/BE/src/users/users.dto.ts index 1e346f5..0d819d6 100644 --- a/BE/src/users/users.dto.ts +++ b/BE/src/users/users.dto.ts @@ -6,7 +6,9 @@ export class CreateUserDto { userId: string; @IsString() - @Matches("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$") + @Matches(RegExp(/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/), { + message: "적절하지 않은 이메일 양식입니다.", + }) email: string; @IsString() diff --git a/BE/src/users/users.repository.ts b/BE/src/users/users.repository.ts index 5f8d1d9..448f067 100644 --- a/BE/src/users/users.repository.ts +++ b/BE/src/users/users.repository.ts @@ -9,11 +9,16 @@ import * as bcrypt from "bcryptjs"; export class UsersRepository { async createUser(createUserDto: CreateUserDto): Promise { - const { userId, password, nickname } = createUserDto; + const { userId, password, nickname, email } = createUserDto; const salt = await bcrypt.genSalt(); const hashedPassword = await bcrypt.hash(password, salt); - const user = User.create({ userId, password: hashedPassword, nickname }); + const user = User.create({ + userId, + password: hashedPassword, + nickname, + email, + }); try { await user.save(); From f8dac05199283e55cf212071513e59584369e126 Mon Sep 17 00:00:00 2001 From: JoonSoo-Kim Date: Tue, 21 Nov 2023 18:33:43 +0900 Subject: [PATCH 18/24] =?UTF-8?q?refactor:=20=EB=AA=A8=EC=96=91=20?= =?UTF-8?q?=EC=97=94=ED=8B=B0=ED=8B=B0=EC=9D=98=20user=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 모양 엔티티의 user의 타입을 Promise에서 User로 변경 - 엔티티 변경사항을 repository에 업데이트 --- BE/src/shapes/shapes.entity.ts | 2 +- BE/src/shapes/shapes.repository.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/BE/src/shapes/shapes.entity.ts b/BE/src/shapes/shapes.entity.ts index 4898dbe..97408eb 100644 --- a/BE/src/shapes/shapes.entity.ts +++ b/BE/src/shapes/shapes.entity.ts @@ -20,7 +20,7 @@ export class Shape extends BaseEntity { uuid: string; @ManyToOne(() => User, (user) => user.userId, { nullable: false }) - user: Promise; + user: User; @Column() shapePath: string; diff --git a/BE/src/shapes/shapes.repository.ts b/BE/src/shapes/shapes.repository.ts index 95c72e9..883e70e 100644 --- a/BE/src/shapes/shapes.repository.ts +++ b/BE/src/shapes/shapes.repository.ts @@ -13,7 +13,7 @@ export class ShapesRepository { if (existingShape) return; - defaultShape.user = Promise.resolve(commonUser); + defaultShape.user = commonUser; const shape = Shape.create(defaultShape); await shape.save(); }), From b76fec32efe91855458bfec5d5f12b11d6d50127 Mon Sep 17 00:00:00 2001 From: JoonSoo-Kim Date: Tue, 21 Nov 2023 18:34:41 +0900 Subject: [PATCH 19/24] =?UTF-8?q?style:=20typeorm.config.ts=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - typeorm.config.ts파일에 있던 typeORMTestConfig를 typeorm.test.config.ts 파일을 생성하여 이동 --- BE/src/configs/typeorm.config.ts | 12 ------------ BE/src/configs/typeorm.test.config.ts | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 12 deletions(-) create mode 100644 BE/src/configs/typeorm.test.config.ts diff --git a/BE/src/configs/typeorm.config.ts b/BE/src/configs/typeorm.config.ts index 9412a77..1a5c037 100644 --- a/BE/src/configs/typeorm.config.ts +++ b/BE/src/configs/typeorm.config.ts @@ -12,15 +12,3 @@ export const typeORMConfig: TypeOrmModuleOptions = { synchronize: false, timezone: "+09:00", }; - -export const typeORMTestConfig: TypeOrmModuleOptions = { - type: "mysql", - host: process.env.DB_HOST, - port: 3306, - username: process.env.DB_USER, - password: process.env.DB_PASS, - database: process.env.TEST_DB_NAME, - entities: ["src/**/*.entity{.ts,.js}"], - synchronize: true, - timezone: "+09:00", -}; diff --git a/BE/src/configs/typeorm.test.config.ts b/BE/src/configs/typeorm.test.config.ts new file mode 100644 index 0000000..896bede --- /dev/null +++ b/BE/src/configs/typeorm.test.config.ts @@ -0,0 +1,14 @@ +import "dotenv/config"; +import { TypeOrmModuleOptions } from "@nestjs/typeorm"; + +export const typeORMTestConfig: TypeOrmModuleOptions = { + type: "mysql", + host: process.env.DB_HOST, + port: 3306, + username: process.env.DB_USER, + password: process.env.DB_PASS, + database: process.env.TEST_DB_NAME, + entities: ["src/**/*.entity{.ts,.js}"], + synchronize: true, + timezone: "+09:00", +}; From 1bb072a8befb3e25348f80e86a7062dd586e4658 Mon Sep 17 00:00:00 2001 From: JoonSoo-Kim Date: Tue, 21 Nov 2023 18:35:35 +0900 Subject: [PATCH 20/24] =?UTF-8?q?feat:=20=EC=8B=A4=ED=96=89=20=ED=99=98?= =?UTF-8?q?=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=A5=B8=20TypeormConfig=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - app.module에서 실행 환경에 따라 typeORMConfig와 typeORMTestConfig 를 골라 실행하도록 변경 --- BE/src/app.module.ts | 6 +++++- BE/test/app.e2e-spec.ts | 4 +--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/BE/src/app.module.ts b/BE/src/app.module.ts index fbbc3fe..65cdb97 100644 --- a/BE/src/app.module.ts +++ b/BE/src/app.module.ts @@ -1,3 +1,4 @@ +import "dotenv/config"; import { Module } from "@nestjs/common"; import { TypeOrmModule } from "@nestjs/typeorm"; import { typeORMConfig } from "./configs/typeorm.config"; @@ -8,10 +9,13 @@ import { IntroduceModule } from "./introduce/introduce.module"; import { ShapesModule } from "./shapes/shapes.module"; import { ShapesRepository } from "./shapes/shapes.repository"; import { UsersRepository } from "./users/users.repository"; +import { typeORMTestConfig } from "./configs/typeorm.test.config"; @Module({ imports: [ - TypeOrmModule.forRoot(typeORMConfig), + TypeOrmModule.forRoot( + process.env.NODE_ENV === "test" ? typeORMTestConfig : typeORMConfig, + ), UsersModule, DiariesModule, AuthModule, diff --git a/BE/test/app.e2e-spec.ts b/BE/test/app.e2e-spec.ts index fd5d51a..b64a1b9 100644 --- a/BE/test/app.e2e-spec.ts +++ b/BE/test/app.e2e-spec.ts @@ -2,15 +2,13 @@ import { Test, TestingModule } from "@nestjs/testing"; import { INestApplication } from "@nestjs/common"; import * as request from "supertest"; import { AppModule } from "../src/app.module"; -import { TypeOrmModule } from "@nestjs/typeorm"; -import { typeORMTestConfig } from "src/configs/typeorm.config"; describe("AppController (e2e)", () => { let app: INestApplication; beforeEach(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ - imports: [TypeOrmModule.forRoot(typeORMTestConfig), AppModule], + imports: [AppModule], }).compile(); app = moduleFixture.createNestApplication(); From e995e29d94dacbd6c03fda10191f57c82e780ed4 Mon Sep 17 00:00:00 2001 From: JoonSoo-Kim Date: Tue, 21 Nov 2023 18:56:03 +0900 Subject: [PATCH 21/24] =?UTF-8?q?feat:=20cross-env=EB=A5=BC=20=ED=86=B5?= =?UTF-8?q?=ED=95=B4=20NODE=5FENV=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - cross-env 설치 - NODE_ENV를 실행 환경마다 다르게 설정 --- BE/package-lock.json | 21 ++++++++++++++++++++- BE/package.json | 11 ++++++----- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/BE/package-lock.json b/BE/package-lock.json index 8a39427..80e3941 100644 --- a/BE/package-lock.json +++ b/BE/package-lock.json @@ -18,9 +18,9 @@ "@nestjs/typeorm": "^10.0.0", "@types/dotenv": "^8.2.0", "@types/passport-jwt": "^3.0.13", + "aws-sdk": "^2.1499.0", "bcryptjs": "^2.4.3", "class-transformer": "^0.5.1", - "aws-sdk": "^2.1499.0", "class-validator": "^0.14.0", "dotenv": "^16.3.1", "mysql2": "^3.6.3", @@ -40,6 +40,7 @@ "@types/supertest": "^2.0.12", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", + "cross-env": "^7.0.3", "eslint": "^8.42.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-prettier": "^5.0.0", @@ -3758,6 +3759,24 @@ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", "devOptional": true }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", diff --git a/BE/package.json b/BE/package.json index 4daffd2..6cc3a27 100644 --- a/BE/package.json +++ b/BE/package.json @@ -8,16 +8,16 @@ "scripts": { "build": "nest build", "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", - "start": "nest start", - "start:dev": "nest start --watch", + "start": "cross-env NODE_ENV=production nest start", + "start:dev": "cross-env NODE_ENV=dev nest start --watch", "start:debug": "nest start --debug --watch", "start:prod": "node dist/main", "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", - "test": "jest", + "test": "cross-env NODE_ENV=test jest", "test:watch": "jest --watch", "test:cov": "jest --coverage", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "jest --config ./test/jest-e2e.json" + "test:e2e": "cross-env NODE_ENV=test jest --config ./test/jest-e2e.json" }, "dependencies": { "@nestjs/common": "^10.0.0", @@ -29,9 +29,9 @@ "@nestjs/typeorm": "^10.0.0", "@types/dotenv": "^8.2.0", "@types/passport-jwt": "^3.0.13", + "aws-sdk": "^2.1499.0", "bcryptjs": "^2.4.3", "class-transformer": "^0.5.1", - "aws-sdk": "^2.1499.0", "class-validator": "^0.14.0", "dotenv": "^16.3.1", "mysql2": "^3.6.3", @@ -51,6 +51,7 @@ "@types/supertest": "^2.0.12", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", + "cross-env": "^7.0.3", "eslint": "^8.42.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-prettier": "^5.0.0", From 30c5e9d594509b56e68dac4d48277d78ddd31d77 Mon Sep 17 00:00:00 2001 From: dbwhdtjr0457 Date: Tue, 21 Nov 2023 22:57:42 +0900 Subject: [PATCH 22/24] =?UTF-8?q?feat:=20react=20query=EB=A1=9C=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=97=B0=EB=8F=99=20=EB=B0=8F=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EA=B7=9C=EC=B9=99=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit useMutation을 이용해 기존 fetch가 하던 데이터 요청을 대체하고 아이디, 비밀번호 규칙을 정규표현식을 통해 적용하였습니다. 정상 로그인한 경우 accessToken을 userAtom에 저장하였습니다. --- FE/src/atoms/userAtom.js | 1 + FE/src/components/LoginModal/LoginModal.js | 70 ++++++++++++---------- FE/src/components/SideBar/SideBar.js | 6 +- 3 files changed, 43 insertions(+), 34 deletions(-) diff --git a/FE/src/atoms/userAtom.js b/FE/src/atoms/userAtom.js index 2bfcebd..749f4d0 100644 --- a/FE/src/atoms/userAtom.js +++ b/FE/src/atoms/userAtom.js @@ -4,6 +4,7 @@ const userAtom = atom({ key: "userState", default: { isLogin: false, + accessToken: "", }, }); diff --git a/FE/src/components/LoginModal/LoginModal.js b/FE/src/components/LoginModal/LoginModal.js index 84232fc..8d85802 100644 --- a/FE/src/components/LoginModal/LoginModal.js +++ b/FE/src/components/LoginModal/LoginModal.js @@ -1,6 +1,8 @@ -import React, { useState, useRef, useEffect } from "react"; +import React, { useState, useRef } from "react"; import { useSetRecoilState } from "recoil"; +import { useMutation } from "react-query"; import styled from "styled-components"; +import userAtom from "../../atoms/userAtom"; import ModalWrapper from "../../styles/Modal/ModalWrapper"; import ModalTitle from "../../styles/Modal/ModalTitle"; import ModalButton from "../../styles/Modal/ModalButton"; @@ -8,54 +10,57 @@ import ModalInputBox from "../../styles/Modal/ModalInputBox"; import ModalBackground from "../ModalBackground/ModalBackground"; import kakao from "../../assets/kakao.png"; import naver from "../../assets/naver.png"; -import userAtom from "../../atoms/userAtom"; function LoginModal() { - const setUserState = useSetRecoilState(userAtom); const [userId, setUserId] = useState(""); const [password, setPassword] = useState(""); - const [isValid, setIsValid] = useState(false); + const setUserState = useSetRecoilState(userAtom); const errorRef = useRef(); + const { mutate: login } = useMutation(() => { + fetch("http://223.130.129.145:3005/auth/signin", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ userId, password }), + }) + .then((res) => res.json()) + .then((data) => { + if (data.accessToken) { + setUserState((prev) => ({ + ...prev, + isLogin: true, + accessToken: data.accessToken, + })); + } else { + errorRef.current.innerText = data.message; + } + }); + }); + function checkValid() { if (userId === "") { errorRef.current.innerText = "아이디를 입력해주세요"; return; } + if (password === "") { errorRef.current.innerText = "비밀번호를 입력해주세요"; return; } - errorRef.current.innerText = ""; + const idRegex = /^[A-Za-z0-9_-]{5,20}$/; + const pwRegex = /^[A-Za-z0-9!@#$%^&*()_+=-~]{5,20}$/; + if (!idRegex.test(userId) || !pwRegex.test(password)) { + errorRef.current.innerText = + "아이디 또는 비밀번호를 잘못 입력했습니다. 입력하신 내용을 다시 확인해주세요."; + return; + } - setIsValid(true); + login(); } - useEffect(() => { - // id: commonUser, password: SecretCommon - if (isValid) { - fetch("http://223.130.129.145:3005/auth/signin", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ userId, password }), - }) - .then((res) => res.json()) - .then((res) => { - if (res.accessToken) { - setUserState({ isLogin: true }); - } else { - errorRef.current.innerText = res.message; - } - }) - .catch((err) => { - console.log(err); - }); - } - }, [isValid]); - return ( <> @@ -122,9 +127,10 @@ const InputBar = styled.div` const ModalButtonContainer = styled.div` display: flex; flex-direction: column; - justify-content: space-between; + justify-content: flex-end; width: 100%; - height: 5rem; + gap: 0.5rem; + height: 6rem; margin-top: -1rem; `; diff --git a/FE/src/components/SideBar/SideBar.js b/FE/src/components/SideBar/SideBar.js index 7485319..8e8fa48 100644 --- a/FE/src/components/SideBar/SideBar.js +++ b/FE/src/components/SideBar/SideBar.js @@ -26,9 +26,11 @@ function SideBar() { isSignUp: false, isSideBar: false, }); - setUserState({ + setUserState((prev) => ({ + ...prev, isLogin: false, - }); + accessToken: "", + })); }} > 로그아웃 From eaea3f2c64bef373f3b0925d4715fe0e1f2c9102 Mon Sep 17 00:00:00 2001 From: dbwhdtjr0457 Date: Tue, 21 Nov 2023 23:37:56 +0900 Subject: [PATCH 23/24] =?UTF-8?q?fix:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EC=84=B1=EA=B3=B5=20=EC=8B=9C=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20?= =?UTF-8?q?=EB=AA=A8=EB=8B=AC=20=EB=AA=85=EC=8B=9C=EC=A0=81=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=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/components/LoginModal/LoginModal.js | 14 +++++++++++++- FE/src/components/SideBar/SideBar.js | 7 +++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/FE/src/components/LoginModal/LoginModal.js b/FE/src/components/LoginModal/LoginModal.js index 8d85802..0ba228a 100644 --- a/FE/src/components/LoginModal/LoginModal.js +++ b/FE/src/components/LoginModal/LoginModal.js @@ -3,6 +3,7 @@ import { useSetRecoilState } from "recoil"; import { useMutation } from "react-query"; import styled from "styled-components"; import userAtom from "../../atoms/userAtom"; +import headerAtom from "../../atoms/headerAtom"; import ModalWrapper from "../../styles/Modal/ModalWrapper"; import ModalTitle from "../../styles/Modal/ModalTitle"; import ModalButton from "../../styles/Modal/ModalButton"; @@ -15,6 +16,7 @@ function LoginModal() { const [userId, setUserId] = useState(""); const [password, setPassword] = useState(""); const setUserState = useSetRecoilState(userAtom); + const setHeaderState = useSetRecoilState(headerAtom); const errorRef = useRef(); const { mutate: login } = useMutation(() => { @@ -28,6 +30,11 @@ function LoginModal() { .then((res) => res.json()) .then((data) => { if (data.accessToken) { + setHeaderState((prev) => ({ + ...prev, + isLogin: false, + isSignUp: false, + })); setUserState((prev) => ({ ...prev, isLogin: true, @@ -85,7 +92,12 @@ function LoginModal() {
- checkValid()}> + { + checkValid(); + }} + > 로그인 diff --git a/FE/src/components/SideBar/SideBar.js b/FE/src/components/SideBar/SideBar.js index 8e8fa48..e4e3431 100644 --- a/FE/src/components/SideBar/SideBar.js +++ b/FE/src/components/SideBar/SideBar.js @@ -21,11 +21,10 @@ function SideBar() { { - setHeaderState({ - isLogin: false, - isSignUp: false, + setHeaderState((prev) => ({ + ...prev, isSideBar: false, - }); + })); setUserState((prev) => ({ ...prev, isLogin: false, From 1303f37ef008db12947b33cc8782dff3de5fe0c7 Mon Sep 17 00:00:00 2001 From: dbwhdtjr0457 Date: Tue, 21 Nov 2023 23:40:26 +0900 Subject: [PATCH 24/24] =?UTF-8?q?fix:=20=ED=95=84=EC=9A=94=EC=97=86?= =?UTF-8?q?=EB=8A=94=20=EA=B0=9C=ED=96=89=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FE/src/components/LoginModal/LoginModal.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/FE/src/components/LoginModal/LoginModal.js b/FE/src/components/LoginModal/LoginModal.js index 0ba228a..fac10ea 100644 --- a/FE/src/components/LoginModal/LoginModal.js +++ b/FE/src/components/LoginModal/LoginModal.js @@ -92,12 +92,7 @@ function LoginModal() {
- { - checkValid(); - }} - > + checkValid()}> 로그인