Skip to content

Commit 13b30eb

Browse files
authored
Merge pull request #18 from ccaaffee/feat/nickname
Feat/nickname: 닉네임 기능 추가
2 parents b3b7c51 + 38d9a79 commit 13b30eb

File tree

10 files changed

+136
-3
lines changed

10 files changed

+136
-3
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/*
2+
Warnings:
3+
4+
- A unique constraint covering the columns `[nickname]` on the table `User` will be added. If there are existing duplicate values, this will fail.
5+
6+
*/
7+
-- AlterTable
8+
ALTER TABLE `User` ADD COLUMN `nickname` VARCHAR(191) NULL;
9+
10+
-- CreateIndex
11+
CREATE UNIQUE INDEX `User_nickname_key` ON `User`(`nickname`);
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
# Please do not edit this file manually
22
# It should be added in your version-control system (e.g., Git)
3-
provider = "mysql"
3+
provider = "mysql"

prisma/schema.prisma

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ datasource db {
1616
model User {
1717
uuid String @id @default(uuid())
1818
kakaoId String @unique
19+
nickname String? @unique
1920
createdAt DateTime @default(now())
2021
2122
userCafes UserCafe[]

src/auth/types/userInfo.type.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export type UserInfo = {
22
uuid: string;
33
kakaoId: string;
4+
nickname: string;
45
createdAt: Date;
56
};
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { ApiProperty } from '@nestjs/swagger';
2+
import { IsString, Length } from 'class-validator';
3+
4+
export class UpdateNicknameDto {
5+
@ApiProperty({
6+
type: String,
7+
description: 'New nickname',
8+
example: '커피러버',
9+
minLength: 2,
10+
maxLength: 20,
11+
})
12+
@IsString()
13+
@Length(2, 20)
14+
nickname: string;
15+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { ApiProperty } from '@nestjs/swagger';
2+
3+
export class NicknameDuplicateCheckDto {
4+
@ApiProperty({
5+
type: Boolean,
6+
description: 'Whether nickname is duplicate or not',
7+
example: false,
8+
})
9+
isDuplicate: boolean;
10+
}

src/user/dto/res/userInfo.dto.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ export class UserInfoDto {
1515
})
1616
kakaoId: string;
1717

18+
@ApiProperty({
19+
type: String,
20+
description: "User's nickname",
21+
example: '커피러버',
22+
})
23+
nickname: string;
24+
1825
@ApiProperty({
1926
type: Date,
2027
description: 'Account created Date',

src/user/user.controller.ts

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Controller, Get, UseGuards } from '@nestjs/common';
1+
import { Controller, Get, UseGuards, Body, Patch, Query } from '@nestjs/common';
22
import { UserService } from './user.service';
33
import { JwtAuthGuard } from 'src/auth/jwt.auth.strategy';
44
import { GetUser } from './decorator/get-user.decorator';
@@ -8,10 +8,13 @@ import {
88
ApiInternalServerErrorResponse,
99
ApiOkResponse,
1010
ApiOperation,
11+
ApiQuery,
1112
ApiTags,
1213
ApiUnauthorizedResponse,
1314
} from '@nestjs/swagger';
1415
import { UserInfoDto } from './dto/res/userInfo.dto';
16+
import { UpdateNicknameDto } from './dto/req/updateNickname.dto';
17+
import { NicknameDuplicateCheckDto } from './dto/res/nicknameDuplicateCheck.dto';
1518

1619
@ApiTags('User')
1720
@Controller('user')
@@ -35,4 +38,49 @@ export class UserController {
3538
async getProfile(@GetUser() user: UserInfo): Promise<UserInfo> {
3639
return user;
3740
}
41+
42+
@ApiOperation({
43+
summary: 'check nickname duplicate',
44+
})
45+
@ApiQuery({
46+
name: 'nickname',
47+
type: String,
48+
description: 'Nickname to check',
49+
required: true,
50+
})
51+
@ApiOkResponse({
52+
type: NicknameDuplicateCheckDto,
53+
description: 'Return whether nickname is duplicate or not',
54+
})
55+
@Get('nickname/check')
56+
async checkNicknameDuplicate(
57+
@Query('nickname') nickname: string,
58+
): Promise<NicknameDuplicateCheckDto> {
59+
const isDuplicate = await this.userService.checkNicknameDuplicate(nickname);
60+
return { isDuplicate };
61+
}
62+
63+
@ApiOperation({
64+
summary: 'update my nickname',
65+
})
66+
@ApiOkResponse({
67+
type: UserInfoDto,
68+
description: 'Return updated profile',
69+
})
70+
@ApiUnauthorizedResponse({ description: 'Unauthorized' })
71+
@ApiInternalServerErrorResponse({
72+
description: 'Internal Server Error',
73+
})
74+
@ApiBearerAuth('JWT')
75+
@Patch('nickname')
76+
@UseGuards(JwtAuthGuard)
77+
async updateNickname(
78+
@GetUser() user: UserInfo,
79+
@Body() updateNicknameDto: UpdateNicknameDto,
80+
): Promise<UserInfo> {
81+
return this.userService.updateNickname(
82+
user.uuid,
83+
updateNicknameDto.nickname,
84+
);
85+
}
3886
}

src/user/user.repository.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,18 @@ import { PrismaService } from 'src/prisma/prisma.service';
55
export class UserRepository {
66
constructor(private readonly prismaService: PrismaService) {}
77

8+
private async generateUniqueNickname(): Promise<string> {
9+
const totalUsers = await this.prismaService.user.count();
10+
const newUserNumber = totalUsers + 1;
11+
return `사용자${newUserNumber}`;
12+
}
13+
814
async createUser(kakaoId: string) {
15+
const nickname = await this.generateUniqueNickname();
916
return this.prismaService.user.create({
1017
data: {
1118
kakaoId,
19+
nickname,
1220
},
1321
});
1422
}
@@ -26,4 +34,19 @@ export class UserRepository {
2634
where: { uuid },
2735
});
2836
}
37+
38+
// 닉네임으로 유저 찾기
39+
async findByNickname(nickname: string) {
40+
return this.prismaService.user.findUnique({
41+
where: { nickname },
42+
});
43+
}
44+
45+
// 닉네임 업데이트
46+
async updateNickname(uuid: string, nickname: string) {
47+
return this.prismaService.user.update({
48+
where: { uuid },
49+
data: { nickname },
50+
});
51+
}
2952
}

src/user/user.service.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Injectable } from '@nestjs/common';
1+
import { ConflictException, Injectable } from '@nestjs/common';
22
import { UserRepository } from './user.repository';
33

44
@Injectable()
@@ -16,4 +16,21 @@ export class UserService {
1616
async findById(uuid: string) {
1717
return this.userRepository.findByUuid(uuid);
1818
}
19+
20+
// 닉네임 중복 확인
21+
async checkNicknameDuplicate(nickname: string): Promise<boolean> {
22+
const existingUser = await this.userRepository.findByNickname(nickname);
23+
return !!existingUser;
24+
}
25+
26+
// 닉네임 업데이트
27+
async updateNickname(uuid: string, nickname: string) {
28+
// 닉네임 중복 확인
29+
const isDuplicate = await this.checkNicknameDuplicate(nickname);
30+
if (isDuplicate) {
31+
throw new ConflictException('이미 사용 중인 닉네임입니다.');
32+
}
33+
34+
return this.userRepository.updateNickname(uuid, nickname);
35+
}
1936
}

0 commit comments

Comments
 (0)