[iOS] ํ•œ์„ฑ๋Œ€ ๊ธฐ์ˆ™์‚ฌ ์–ดํ”Œ ํ”„๋กœ์ ํŠธ

2025. 2. 13. 00:18ยทPROJECT๐Ÿ’ป

ํ•ด๋‹น ํ”„๋กœ์ ํŠธ 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. ๊ณต์ง€์‚ฌํ•ญ

  • ๊ด€๋ฆฌ์ž์ธก์—์„œ ๊ธฐ์ˆ™์‚ฌ์™€ ๊ด€๋ จ๋œ ๊ณต์ง€์‚ฌํ•ญ์„ ์˜ฌ๋ฆฌ๋ฉด ํ•™์ƒ๋“ค์€ ์ด ๊ณต์ง€์‚ฌํ•ญ๋“ค์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๊ณต์ง€์‚ฌํ•ญ์€ ๊ฐ€์žฅ ์˜ค๋ž˜๋œ ๊ธ€๋ถ€ํ„ฐ ๋‚ด๋ฆผ์ฐจ์ˆœ์œผ๋กœ ์˜ฌ๋ ค์ ธ ์žˆ๊ณ , ํ•™์ƒ๋“ค์€ ๊ฒ€์ƒ‰์„ ํ†ตํ•ด ๊ถ๊ธˆํ•œ ๊ณต์ง€ ์‚ฌํ•ญ์„ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

'PROJECT๐Ÿ’ป' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

[Web] ์ž์ทจ๋งŒ๋ ™ ํ”„๋กœ์ ํŠธ  (0) 2025.02.11
[Android] Green Market ํ”„๋กœ์ ํŠธ  (0) 2025.02.10
'PROJECT๐Ÿ’ป' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
  • [Web] ์ž์ทจ๋งŒ๋ ™ ํ”„๋กœ์ ํŠธ
  • [Android] Green Market ํ”„๋กœ์ ํŠธ
์ด๋ฆฌ์ญ
์ด๋ฆฌ์ญ
ํ”„๋กœ์ ํŠธ ์ •๋ฆฌ, ๊ณต๋ถ€ ์ •๋ฆฌ ๋“ฑ๋“ฑ ํ•ฉ๋‹ˆ๋‹น๐Ÿถ / ๊นƒํ—ˆ๋ธŒ id: ssarisong
  • ์ด๋ฆฌ์ญ
    ๐Ÿ„DEV์ญ๐Ÿ„
    ์ด๋ฆฌ์ญ
  • ์ „์ฒด
    ์˜ค๋Š˜
    ์–ด์ œ
    • ๋ถ„๋ฅ˜ ์ „์ฒด๋ณด๊ธฐ (18)
      • PROJECT๐Ÿ’ป (3)
      • STUDY๐Ÿ„ (15)
  • ๋ธ”๋กœ๊ทธ ๋ฉ”๋‰ด

    • ํ™ˆ
    • ํƒœ๊ทธ
    • ๋ฐฉ๋ช…๋ก
  • ๋งํฌ

  • ๊ณต์ง€์‚ฌํ•ญ

  • ์ธ๊ธฐ ๊ธ€

  • ํƒœ๊ทธ

    ๊ฐ„๋‹จํ•œ ๊ฐœ๋ฐœ
    ํ•œ์„ฑ๋Œ€ํ•™๊ต
    TypeScript
    ํ•œ์„ฑ๋Œ€ ๊ธฐ์ˆ™์‚ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜
    ์Šคํ”„๋ง
    ์‹ฑ๊ธ€ํ†ค ์ฃผ์˜์ 
    ์Šคํ”„๋ง ๋นˆ ์กฐํšŒ
    ์กฐํšŒ ๋นˆ์ด 2๊ฐœ ์ด์ƒ
    backend
    ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ
    spring
    filtertype
    ์ˆ˜๋™ ๋นˆ ๋“ฑ๋ก vs ์ž๋™ ๋นˆ ๋“ฑ๋ก
    ํ•œ์„ฑ๋Œ€
    ์ค‘๊ณ ๋งˆ์ผ“ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜
    ์‹ฑ๊ธ€ํ†ค
    ์บ์‹œ ๋ฌดํšจํ™”
    Java
    @Autowired
    http
  • ์ตœ๊ทผ ๋Œ“๊ธ€

  • ์ตœ๊ทผ ๊ธ€

  • hELLOยท Designed By์ •์ƒ์šฐ.v4.10.3
์ด๋ฆฌ์ญ
[iOS] ํ•œ์„ฑ๋Œ€ ๊ธฐ์ˆ™์‚ฌ ์–ดํ”Œ ํ”„๋กœ์ ํŠธ
์ƒ๋‹จ์œผ๋กœ

ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”