[iOS] νμ±λ κΈ°μμ¬ μ΄ν νλ‘μ νΈ
ν΄λΉ νλ‘μ νΈ GitHub Repository
https://github.com/HansungDomitory/HansungDomitory_Backend
GitHub - HansungDomitory/HansungDomitory_Backend
Contribute to HansungDomitory/HansungDomitory_Backend development by creating an account on GitHub.
github.com
1. μ£Όμ μ€λͺ
νμ±λνκ΅ μ¬λ¦λ°©ν μ§λ‘νμνμ μ νλ‘μ νΈμ λλ€.
νκ΅μμ λ°©νλμ νλ νΉλ³ν νλμΈ λ§νΌ μ΄ κΈ°κ° λμ κΌ λ§λ€μ΄λ³΄κ³ μΆμμΌλ©΄μλ νκ΅μλ λμμ΄ λ κ² κ°μ μ£Όμ λ‘ νκ³ μΆλ€λ μκ°μ΄ λ€μμ΅λλ€.
κ·Έλ¬λ€κ° νκ΅ κΈ°μμ¬ μ΄ν리μΌμ΄μ μ μ¬μ©νλλ° λΆνΈν μ μ΄ κ½€ λ§μλ κΈ°μ΅μ΄ μμ΄ μ΄λ₯Ό λͺ¨ν°λΈνλ©΄ μ’μ κ² κ°μ μ£Όμ λ₯Ό νμ±λ κΈ°μμ¬ μ΄νλ‘ μ μ νκ² λμμ΅λλ€.
2. κ°λ° κΈ°κ°
2024.07.04 ~ 2024.08.05 (μ½ 1κ°μ)
3. μν
λ°±μλ, λ°°ν¬ μλ² κ΄λ¦¬
- μ€κ³
- ERD μ€κ³
- λ‘κ·ΈμΈ κ³Όμ μ€κ³
- ꡬν
- λ‘κ·ΈμΈ κΈ°λ₯ ꡬν
- μΈλ°μ μ² κΈ°λ₯ ꡬν
- μλ²μ κΈ°λ₯ ꡬν
- 곡μ§μ¬ν κΈ°λ₯ ꡬν
- λ°°ν¬
- AWS EC2 μΈμ€ν΄μ€ λ°°ν¬
4. κΈ°μ μ€ν
- μΈμ΄: TypeScript
- νλ μμν¬: NestJS
- λ°μ΄ν°λ² μ΄μ€: MySQL
- λ°°ν¬: AWS EC2
- λꡬ: Docker, RestAPI, Swagger
5. ERD
6. μ 체 ꡬ쑰
7. μμΈ κ΅¬ν λ΄μ©
NestJS νκ²½μμ RestAPIλ‘ λ°±μλ APIλ₯Ό ꡬννμ΅λλ€.
7-1. νμ κ΄λ¦¬
- JWT ν ν°μ νμ©ν λ‘κ·ΈμΈ κ΄λ¦¬
JWT ν ν°μ λ§λ€μ΄μ νμμ λ‘κ·ΈμΈμ κ΄λ¦¬νμ΅λλ€.
JWT ν ν°μ λ‘κ·ΈμΈ μ²λ¦¬λ₯Ό λ΄λΉνλ accessTokenκ³Ό accessToken κΈ°κ° λ§λ£ μ μ¬λ°κΈμ λ΄λΉνλ refreshTokenμΌλ‘ λ§λ€μμ΅λλ€.
refreshTokenμ Cookieλ₯Ό ν΅ν΄ ν΄λΌμ΄μΈνΈμ μ λ¬νλ λ°©μμ μ¬μ©νμμ΅λλ€.
[accessToken λ°κΈ]
getAccessToken(student: Student | IStudentContext['student']): string {
return this.jwtService.sign(
{ sub: student.id },
{ secret: process.env.JWT_ACCESS_SECRET, expiresIn: '100m' }
);
}
[refreshToken λ°κΈ]
getRefreshToken(student: Student, context: IContext): void {
const refreshToken = this.jwtService.sign(
{ sub: student.id },
{ secret: process.env.JWT_REFRESH_SECRET, expiresIn: '2w' },
);
context.res.setHeader(
'set-cookie',
`refreshToken=${refreshToken}; path=/;`,
);
}
- μμ μ μΈ λΉλ°λ²νΈ κ΄λ¦¬
νμμ΄ νμκ°μ μ ν λ νλ²κ³Ό λΉλ°λ²νΈλ₯Ό μ λ ₯νκΈ° λλ¬Έμ λΉλ°λ²νΈλ₯Ό λ°μ΄ν°λ² μ΄μ€μ μμ μ μΌλ‘ μ μ₯νλ κ³Όμ μ΄ νμνμ΅λλ€.
bcryptμ hash κΈ°λ₯μ μ¬μ©ν΄μ λΉλ°λ²νΈλ₯Ό hash μ²λ¦¬ν ν μ μ₯νμκ³ , λΉλ°λ²νΈ νμΈ μμλ hash λ λΉλ°λ²νΈλ₯Ό 볡νΈννμ¬ λ§€μΉμμΌ°μ΅λλ€.
[νμκ°μ μ λΉλ°λ²νΈ hash μ²λ¦¬]
async create({ createStudentInput }: IStudentServiceCreate): Promise<Student> {
...
const { password, ...rest} = createStudentInput;
const hashedPassword = await bcrypt.hash(password, 10);
...
}
7-2. κ΄λ¦¬μ μ€μ
μλ²μ κ΄λ¦¬μ 곡μ§μ¬ν μμ±μ κ΄λ¦¬μλ§ μ΄μ©ν μ μμ΄μΌ νκΈ° λλ¬Έμ νμκ°μ μ μ‘΄μ¬ν μ μλ νλ²μΈ '000000'μ κ°μ§ μ¬μ©μλ₯Ό κ΄λ¦¬μλ‘ λ±λ‘νμ΅λλ€.
κ·Έλ¦¬κ³ , μ μμ μΈ κ²½λ‘λ‘ λ§λ€ μ μλ κ΄λ¦¬μ κ³μ μ λ°μ΄ν°λ² μ΄μ€μμ μ§μ νλ²μ μμ νμ¬ λ§λ€μμ΅λλ€.
[νμκ°μ μ κ΄λ¦¬μ κ³μ μμ± λ°©μ§]
async create({ createStudentInput }: IStudentServiceCreate): Promise<Student> {
...
if(createStudentInput.id === '0000000') {
throw new BadRequestException('νμκ°μ
ν μ μλ IDμ
λλ€.');
}
…
}
κ΄λ¦¬μλ§ νΈμΆν μ μλ APIμ νΈμΆν μ¬μ©μκ° κ΄λ¦¬μμΈμ§ νμΈνλ μμ μ μΆκ°νκ³ , κ΄λ¦¬μκ° μλ μμ ForbiddenException μμΈλ₯Ό λ°μμμΌμ μΌλ° μ¬μ©μκ° μ¬μ©ν μ μλλ‘ κ΅¬ννμ΅λλ€.
[μλ²μ μΆκ° APIμμ κ΄λ¦¬μ νμΈ μ μ°¨ μΆκ°]
async create(myId: string, createScoreRecordInput: CreateScoreRecordInput) {
if(!myId || myId !== '0000000') {
throw new ForbiddenException('κ΄λ¦¬μλ§ μλ²μ κΈ°λ‘μ μμ±ν μ μμ΅λλ€.');
}
…
}
7-3. λλ€ λ°© λ°°μ
κΈ°μμ¬ μ΄ν리μΌμ΄μ μ κ°μ ν νμλ€μ κ°μ λΉ λ°© λ²νΈκ° λ°°μ μ΄ λμ΄μΌ ν©λλ€.
λ°λΌμ, λ°°μ μ΄ λμ§ μμ λΉ λ°©μ μ°Ύλ κ³Όμ κ³Ό λ°°μ λμ§ μμ λΉ λ°©μ νμ κ°μ ν νμμκ² λ°°μ νλ κ³Όμ μ ꡬννμ΅λλ€.
[λλ€ λ°© λ²νΈ μμ±]
private generateRandomRoom(): string {
const floor = this.getRandomInt(1, 9);
const roomSuffix = this.getRandomInt(1, 9);
const tailDasi = this.getRandomInt(1, 2);
return `${floor}0${roomSuffix}-${tailDasi}`;
}
[μ¬μ©λμ§ μμ λλ€ λ°© λ²νΈ λ°°μ ]
private async assignRandomRoom(): Promise<string> {
const existingRooms = await this.studentRepository.find({ select: ['room'] });
const existingRoomSet = new Set(existingRooms.map(student => student.room));
while (true) {
const randomRoom = this.generateRandomRoom();
if (!existingRoomSet.has(randomRoom)) {
return randomRoom;
}
}
}
7-4. μλ²μ κ΄λ¦¬
μλ²μ μ κ΄λ¦¬μκ° νμμκ² λΆμ¬νλ λ°©μμΌλ‘ ꡬννμ΅λλ€.
μμ κ³Ό λ²μ μ λ°λ‘ ꡬννμ§ μκ³ , νλμ μ μ νλλ‘ μμ±ν ν μμλ μμ μΌλ‘, μμλ λ²μ μΌλ‘ ꡬννμ΅λλ€.
νλμ νλλ‘λ§ κ΅¬νν μ΄μ
μμ μ΄ μλ μνμμ λ²μ μ΄ λΆμ¬λλ©΄ κ·Έλ§νΌ μμ μ΄ μ°¨κ°λλ κ²μ²λΌ μμ κ³Ό λ²μ μ μλ‘ μ°κ΄λμ΄ μλ μνμ΄κΈ° λλ¬Έμ, μμ κ³Ό λ²μ μ λ°λ‘ λλ μ ꡬννμ§ μκ³ κ°λ¨νκ² μ μλΌλ νλμ νλλ§ κ°κ² νμ΅λλ€.
[μ μ νλ νλλ‘λ§ κ΅¬νν μλ²μ κΈ°λ₯ μν°ν°]
@Entity()
export class ScoreRecord {
@ApiProperty({ description: "κ³ μ λ²νΈ" })
@PrimaryGeneratedColumn('increment')
id: number;
@ApiProperty({ description: "ν΄λΉ κΈ°λ‘ κ΄λ ¨ νμ" })
@ManyToOne(() => Student, { onDelete: 'CASCADE' })
@JoinColumn()
student: Student;
@ApiProperty({ description: "μμ /λ²μ μ¬λΆ" })
@Column()
is_merit: boolean;
@ApiProperty({ description: "μ μ" })
@Column()
score: number;
@ApiProperty({ description: "μμΈλ΄μ©" })
@Column()
detail: string;
@ApiProperty({ description: "μμ±μΌμ" })
@CreateDateColumn()
create_at: Date;
}
8. μ€ν μλ리μ€
8-1. νμκ°μ , λ‘κ·ΈμΈ
- νμκ°μ
: μ΄λ¦, νλ², λΉλ°λ²νΈλ₯Ό λ€ μ
λ ₯ν΄μΌ νμκ°μ
μ΄ κ°λ₯ν©λλ€.
- νμκ°μ μ νλ©΄ λλ€μΌλ‘ κΈ°μμ¬ λΉ λ°©μ΄ λ°°μ λ©λλ€.
- λ‘κ·ΈμΈ: νμκ°μ ν, νλ²κ³Ό λΉλ°λ²νΈλ₯Ό μ λ ₯νλ©΄ λ‘κ·ΈμΈμ΄ λ©λλ€.
8-2. ν νλ©΄ λ° κΈ°μμ¬ μκ° νλ©΄
- ν νλ©΄: λ©μΈ νλ©΄μΌλ‘, ν΄λΉ νμ΄μ§μμ κΈ°μμ¬ μκ° νμ΄μ§μ 곡μ§μ¬ν νμ΄μ§λ‘ μ΄λν μ μμ΅λλ€.
- κΈ°μμ¬ μκ° νλ©΄: νμ±λνκ΅ κΈ°μμ¬μ κ΄ν μ λ³΄κ° λμμμ΅λλ€.
8-3. μΈλ°μ μ²
- μ΄ νμ΄μ§μμ μΈλ° μ μ²μ΄ κ°λ₯νλ©°, νλ²κ³Ό μ΄λ¦μ μλμΌλ‘ λΆλ¬μμ§κ³ λ μ§λ₯Ό μ ννλ©΄ μλμΌλ‘ μΈλ°μΌμκ° λ³΄μ¬μ§λλ€.
- λ μ§λ₯Ό μ ννκ³ λ΄μ©κΉμ§ μ μ΄μ μ μ²νκΈ° λ²νΌμ λλ₯΄λ©΄ μΈλ° νν© νμ΄μ§κ° λμ€κ³ , κ·Έ νμ΄μ§μμ κΈ°κ°κ³Ό μΈλ°μΌμ, λ±λ‘μΌμλ₯Ό νμΈν μ μμ΅λλ€.
- μΈλ° μ μ²νλ λͺ©λ‘λ€μ λ±λ‘μΌμ κΈ°μ€μΌλ‘ λ΄λ¦Όμ°¨μμΌλ‘ λ°°μ΄λμ΄ μμΌλ©°, μΈλ° κΈ°κ°μ΄ λκΈ° μ μλ μμ κ³Ό μμ κ° κ°λ₯ν©λλ€.
8-4. λ§μ΄νμ΄μ§, μλ²μ μ‘°ν
- λ§μ΄νμ΄μ§
- νμ μ΄λ¦, λ°°μ λ λ°©μ΄ λμ μμΌλ©°, ν΄λΉ νμ΄μ§μμ λ‘κ·Έμμ λ²νΌμ λλ₯΄λ©΄ λ‘κ·Έμμμ΄ κ°λ₯ν©λλ€.
- ν΄λΉ νμ΄μ§μμ 곡μ§μ¬ν νμ΄μ§, μΈλ°νν© νμ΄μ§, μλ²μ νν© νμ΄μ§λ‘ μ΄λν μ μμ΅λλ€.
- μλ²μ μ‘°ν
- νμ μ 보μ μλ²μ μ μ, μ΄μ , μλ²μ μ κ΄ν μ¬μ μ μ μ©μΌμκ° λμμκ³ λ΄λ¦Όμ°¨μμΌλ‘ λμ΄λμ΄ μμ΅λλ€.
- νλμ +μ μλ κΈ°μ‘΄ μ μμμ κ°μ°λκ³ , λΉ¨κ°μ -μ μλ κΈ°μ‘΄ μ μμμ κ°μ°λ©λλ€.
8-5. 곡μ§μ¬ν
- κ΄λ¦¬μμΈ‘μμ κΈ°μμ¬μ κ΄λ ¨λ 곡μ§μ¬νμ μ¬λ¦¬λ©΄ νμλ€μ μ΄ κ³΅μ§μ¬νλ€μ νμΈν μ μμ΅λλ€.
- 곡μ§μ¬νμ κ°μ₯ μ€λλ κΈλΆν° λ΄λ¦Όμ°¨μμΌλ‘ μ¬λ €μ Έ μκ³ , νμλ€μ κ²μμ ν΅ν΄ κΆκΈν κ³΅μ§ μ¬νμ λ³Ό μ μμ΅λλ€.