44,000원
다른 수강생들이 자주 물어보는 질문이 궁금하신가요?
- 미해결Slack 클론 코딩[백엔드 with NestJS + TypeORM]
레포지토리 분리 질문입니다.
스프링에서 일반적으로 레포지토리 클래스를 분리하여 따로 구현하는 것으로 알고 있는데 nest에서는 그렇게 진행하면 레포지토리에 구현된 함수들을 읽어오지 못하는 현상이 있습니다.현재 강의 구조에서 레포지토리를 분리하는 방법을 알려주시면 감사하겠습니다!
- 미해결Slack 클론 코딩[백엔드 with NestJS + TypeORM]
화면에 Cannot read properties of undefined (reading 'map') 에러
빠진 컨트롤러 구현하기 강의 도중 로그인 시 다음과 같은 화면이 뜨는데 어느 부분이 문제인지 궁금합니다네트워크 탭 확인해보면 요청에는 다 응답이 오고 빈값도 없습니다 심지어는 백엔드나 프런트 로그에도 에러가 없어서 문제를 모르겠습니다.. React 에러는 front/layouts/Workspace/index.ts 파일의 <Workspaces> {userData?.Workspaces.map((ws) => { console.log(ws); return ( <Link key={ws.id} to={`/workspace/${ws.url}/channel/일반`}> <WorkspaceButton>{ws.name.slice(0, 1).toUpperCase()}</WorkspaceButton> </Link> ); })} <AddButton onClick={onClickCreateWorkspace}>+</AddButton> </Workspaces> map 함수 부분에서 에러가 나는거 같습니다.
- 미해결Slack 클론 코딩[백엔드 with NestJS + TypeORM]
loggerMiddleware로 morgan처럼 로깅하기 수업에서 @Injectable 부분 에러
LoggerMiddleware class를 작성하던 도중 @Injectable 데코레이터에서 2개의 에러가 납니다. 하나는 Decorator function return type 'ClassDecorator' is not assignable to type 'void | typeof LoggerMiddleware'. 데코레이터 함수 반환 유형 'ClassDecorator'은(는) 'void | typeof LoggerMiddleware' 유형에 할당할 수 없습니다. 라는 에러와 Type 'typeof LoggerMiddleware' has no properties in common with type 'ScopeOptions'. 'typeof LoggerMiddleware' 유형에 'ScopeOptions' 유형과 공통적인 속성이 없습니다. 라는 에러가 납니다 첫번째 에러는 검색하니 주로 데코레이터를 직접 만드신 분들이 내는 에러고 두번째 에러는 타입스크립트 버전을 올리신 분들이 겪는 에러로 보이는데 어떻게 해결할지 모르겠습니다.. 타입스크립트 버전을 내려야 할까요? 현재코드는 이렇습니다. import { NestMiddleware, Logger, Injectable } from '@nestjs/common'; import { Request , Response , NextFunction } from 'express'; @Injectable export class LoggerMiddleware implements NestMiddleware { private logger = new Logger('HTTP') use(req: Request, res: Response, next:NextFunction ) : void { const {ip , method , originalUrl } = req ; const userAgent = req.get('user-agent') || ''; res.on('finish', ()=>{ const {statusCode} = res; const contentLength = res.get("content-length") this.logger.log(`${method} ${originalUrl} ${statusCode} ${contentLength} ${userAgent} ${ip}`) }) next() } }
- 미해결Slack 클론 코딩[백엔드 with NestJS + TypeORM]
main.ts에서 session 설정은 어느 강의에서 하나요??
app.use(cookieParser()); app.use( session(...) ) 부분 설정을 어느 강의에서 하나요?? 차근차근 정주행했는데 session 관련 설정하는 부분을 못본것같아서요...
- 미해결Slack 클론 코딩[백엔드 with NestJS + TypeORM]
ValidateIf 미작동에 관한 질문
안녕하세요! 위 코드에서 validateIf가 작동하지 않는데요 파라미터로 값을 받아오는데 existsMemo가 파라미터로 값을 보내줄때만 validateIf를 적용시키고 싶어서요 true일때 나머지 옵션들을 무시하는 기능이라고 알고있는데 혹시 사용법이 잘못됬나해서 질문드립니다.
- 미해결Slack 클론 코딩[백엔드 with NestJS + TypeORM]
스웨거 문서를 PickType으로 만들 수 있나요?
1. Users.ts(엔티티) import { Column, CreateDateColumn, DeleteDateColumn, Entity, Index, JoinTable, ManyToMany, OneToMany, PrimaryGeneratedColumn, UpdateDateColumn, } from 'typeorm'; import { ChannelChats } from './ChannelChats'; import { ChannelMembers } from './ChannelMembers'; import { Channels } from './Channels'; import { DMs } from './DMs'; import { Mentions } from './Mentions'; import { WorkspaceMembers } from './WorkspaceMembers'; import { Workspaces } from './Workspaces'; import { IsEmail, IsNotEmpty, IsString } from 'class-validator'; import { ApiProperty } from '@nestjs/swagger'; @Index('email', ['email'], { unique: true }) @Entity({ schema: 'sleact', name: 'users' }) export class Users { @PrimaryGeneratedColumn({ type: 'int', name: 'id' }) id: number; @ApiProperty({ example: `aaa123@google.com`, description: '이메일', required: true, }) @IsEmail() @IsNotEmpty() @Column('varchar', { name: 'email', unique: true, length: 30 }) email: string; @ApiProperty({ example: `홍길동`, description: '닉네임', required: true, }) @IsString() @IsNotEmpty() @Column('varchar', { name: 'nickname', length: 30 }) nickname: string; @ApiProperty({ example: `123123`, description: '비밀번호', required: true, }) @IsString() @IsNotEmpty() @Column('varchar', { name: 'password', length: 100, select: false }) password: string; @CreateDateColumn() createdAt: Date; @UpdateDateColumn() updatedAt: Date; @DeleteDateColumn() deletedAt: Date | null; @OneToMany(() => ChannelChats, (channelchats) => channelchats.User) ChannelChats: ChannelChats[]; @OneToMany(() => ChannelMembers, (channelmembers) => channelmembers.User) ChannelMembers: ChannelMembers[]; @OneToMany(() => DMs, (dms) => dms.Sender) DMs: DMs[]; @OneToMany(() => DMs, (dms) => dms.Receiver) DMs2: DMs[]; @OneToMany(() => Mentions, (mentions) => mentions.Sender) Mentions: Mentions[]; @OneToMany(() => Mentions, (mentions) => mentions.Receiver) Mentions2: Mentions[]; @OneToMany( () => WorkspaceMembers, (workspacemembers) => workspacemembers.User, ) WorkspaceMembers: WorkspaceMembers[]; @OneToMany(() => Workspaces, (workspaces) => workspaces.Owner) OwnedWorkspaces: Workspaces[]; @ManyToMany(() => Workspaces, (workspaces) => workspaces.Members) @JoinTable({ name: 'workspacemembers', joinColumn: { name: 'UserId', referencedColumnName: 'id', }, inverseJoinColumn: { name: 'WorkspaceId', referencedColumnName: 'id', }, }) Workspaces: Workspaces[]; @ManyToMany(() => Channels, (channels) => channels.Members) @JoinTable({ name: 'channelmembers', joinColumn: { name: 'UserId', referencedColumnName: 'id', }, inverseJoinColumn: { name: 'ChannelId', referencedColumnName: 'id', }, }) Channels: Channels[]; } 2. join.request.dto import { PickType } from '@nestjs/mapped-types'; import { Users } from '../../entities/Users'; export class JoinRequestDto extends PickType(Users, [ 'email', 'nickname', 'password', ] as const) {} 3. user.dto import { JoinRequestDto } from './join.request.dto'; import { ApiProperty } from '@nestjs/swagger'; export class UserDto extends JoinRequestDto { @ApiProperty({ example: `1`, description: '아이디', required: true, }) id: number; } 4. users.controller import { Body, Controller, ForbiddenException, Get, NotFoundException, Post, Req, Res, UseGuards, UseInterceptors, } from '@nestjs/common'; import { UsersService } from './users.service'; import { JoinRequestDto } from './dto/join.request.dto'; import { User } from '../common/decorators/user.decorator'; import { UndefinedToNullInterceptor } from '../common/interceptors/undefinedToNull.interceptor'; import { LocalAuthGuard } from '../auth/local-auth.guard'; import { NotLoggedInGuard } from '../auth/not-logged-in.guard'; import { LoggedInGuard } from '../auth/logged-in.guard'; import { ApiCookieAuth, ApiOperation, ApiResponse, ApiTags, } from '@nestjs/swagger'; import { Users } from '../entities/Users'; import { UserDto } from './dto/user.dto'; @ApiTags('USERS') @UseInterceptors(UndefinedToNullInterceptor) @Controller('api/users') export class UsersController { constructor(private readonly usersService: UsersService) {} @ApiCookieAuth('connect.sid') @ApiOperation({ summary: '내 정보 가져오기' }) @ApiResponse({ type: UserDto, }) @Get() async getMyProfile(@User() user: Users) { return user || false; } @ApiResponse({ status: 500, description: 'Server Error..', }) @ApiResponse({ status: 200, description: '성공!', }) @ApiOperation({ summary: '회원가입' }) @UseGuards(NotLoggedInGuard) @Post() async join(@Body() body: JoinRequestDto) { const user = this.usersService.findByEmail(body.email); if (!user) { throw new NotFoundException(); } const result = await this.usersService.join( body.email, body.nickname, body.password, ); if (result) { return 'ok'; } else { throw new ForbiddenException(); } } @ApiResponse({ status: 200, description: '성공', type: UserDto, }) @ApiOperation({ summary: '로그인' }) @UseGuards(LocalAuthGuard) @Post('login') async login(@User() user: Users) { return user; } @ApiCookieAuth('connect.sid') @ApiOperation({ summary: '로그아웃' }) @UseGuards(LoggedInGuard) @Post('logout') async logout(@Req() req, @Res() res) { req.logOut(); res.clearCookie('connect.sid', { httpOnly: true }); res.send('ok'); } } ---------------------------- 스웨거 문서 1. Dto 관련 스키마 2. usersDto를 사용한 결과 3. joinRequestDto를 사용한 결과 마지막 결과 쪽에 제가 생각한 것은 빈칸이 아니라{ email : "aaa123@google.com" nickname: "홍길동" passwork: "123123"} 이었는데 빈칸으로 나오네요.. 혹시 잘못한 부분이 있을까요?
- 해결됨Slack 클론 코딩[백엔드 with NestJS + TypeORM]
dms 의 서비스에서 코드를 봐도 모르겠는 부분이 있어서 질문합니다.
function getKeyByValue(object, value) { return Object.keys(object).find((key) => object[key] === value); } ... async getWorkspaceDMChats( url: string, id: number, myId: number, perPage: number, page: number, ) { return this.dmsRepository .createQueryBuilder('dms') .innerJoinAndSelect('dms.Sender', 'sender') .innerJoinAndSelect('dms.Receiver', 'receiver') .innerJoin('dms.Workspace', 'workspace') .where('workspace.url = :url', { url }) .andWhere( '((dms.SenderId = :myId AND dms.ReceiverId = :id) OR (dms.ReceiverId = :myId AND dms.SenderId = :id))', { id, myId }, ) .orderBy('dms.createdAt', 'DESC') .take(perPage) .skip(perPage * (page - 1)) .getMany(); } ... async createWorkspaceDMChats( url: string, content: string, id: number, myId: number, ) { const workspace = await this.workspacesRepository.findOne({ where: { url }, }); const dm = new DMs(); dm.SenderId = myId; dm.ReceiverId = id; dm.content = content; dm.WorkspaceId = workspace.id; const savedDm = await this.dmsRepository.save(dm); const dmWithSender = await this.dmsRepository.findOne({ where: { id: savedDm.id }, relations: ['Sender'], }); const receiverSocketId = getKeyByValue( onlineMap[`/ws-${workspace.url}`], Number(id), ); this.eventsGateway.server.to(receiverSocketId).emit('dm', dmWithSender); } ... 1. getKeyByValue가 어떠한 역할을 하나요? 소켓을 사용할 때 필요한 부분이라고 짐작만 하고 있습니다. 코드에 대한 간단한 해석과 역할을 간단하게 알려주시면 감사하겠습니다. 2. async getWorkspaceDMChats 기능에서 .innerJoinAndSelect('dms.Sender', 'sender') 부분이 헷갈립니다. dms라는 별칭과 Sender라는 테이블이 join 되는 부분이라고 생각하고 있었는데, Sender 라는 테이블이 존재했었나요?저는 여태까지 dms.Workspaces 이런식으로 DMs와 Workspaces 테이블을 join 하는 부분이라고 생각하고 있었는데 잘못 알고 있는건가요..? 3. async createWorkspaceDMChats 중 const dmWithSender 부분에서 2번과 비슷한 내용의 질문인데, relations: ['Sender']가 나오는데 이 부분 또한 테이블과의 관계를 나타내는 부분으로 알고있었는데 Sender라는 테이블이 존재하는건가요?
- 미해결Slack 클론 코딩[백엔드 with NestJS + TypeORM]
InjectRepository 오류
@Injectable() export class UsersService { constructor( @InjectRepository(Users) private usersRepository: Repository<Users>, @InjectRepository(Workspaces) private workspacesRepository: Repository<Workspaces>, //@InjectRepository(ChannelMembers) //private channelMembersRepository: Repository<ChannelMembers>, //여기 추가하면 user.module.ts의 import에도 넣어줘야한다 /* @InjectRepository(WorkspaceMembers) private workspaceMembersRepository: Repository<WorkspaceMembers>, //여기 추가하면 user.module.ts의 import에도 넣어줘야한다 */ private dataSource: DataSource, ) {} users.service.ts에 TypeORM을 사용하기 위하여 위와 같이 코딩을 하면 ERROR [ExceptionHandler] Nest can't resolve dependencies of the UsersService (UsersRepository, ?, DataSource). Please make sure that the argument WorkspacesRepository at index [1] is available in the AppModule context. Potential solutions: - If WorkspacesRepository is a provider, is it part of the current AppModule? - If WorkspacesRepository is exported from a separate @Module, is that module imported within AppModule? @Module({ imports: [ /* the Module containing WorkspacesRepository */ ] }) 이런 에러가 납니다. 신기한건 Users는 나지 않고 Workspaces, 채널멤버, 워크스페이스 멤버등만 에러가 납니다. 강의에서 해당 에러가 날때는 module을 살펴보라고 하셨지만 users.module.ts @Module({ imports: [ TypeOrmModule.forFeature([ Users, WorkspaceMembers, ChannelMembers, Workspaces, ]), ], providers: [UsersService], controllers: [UsersController], }) export class UsersModule {} import는 시켰구요 app.module.ts에도 @Module({ imports: [ TypeOrmModule.forFeature([Users]), ConfigModule.forRoot({ isGlobal: true, //아래 load는 aws같은데서 config를 받아올때 하는거다 //load: [getEnv], }), AuthModule, UsersModule, WorkspacesModule, ChannelsModule, DmsModule, TypeOrmModule.forRootAsync({ inject: [ConfigService], useFactory: (configService: ConfigService) => { return { type: 'mysql', host: 'localhost', port: 3306, User모듈과 workspaces모듈을 넣었습니다.ㅠㅠㅠㅠ 또 재미있는건 같은 코딩을 workspaces.service.ts에 했을때는 에러가 나지 않는다는 것입니다. workspaces.service.ts @Injectable() export class WorkspacesService { constructor( @InjectRepository(Users) private usersRepository: Repository<Users>, @InjectRepository(Workspaces) private workspacesRepository: Repository<Workspaces>, @InjectRepository(ChannelMembers) private channelMembersRepository: Repository<ChannelMembers>, //여기 추가하면 user.module.ts의 import에도 넣어줘야한다 private dataSource: DataSource, ) {} getUsers() {} workspaces.module.ts @Module({ imports: [ TypeOrmModule.forFeature([ Users, WorkspaceMembers, ChannelMembers, Workspaces, ]), ], providers: [WorkspacesService], controllers: [WorkspacesController], }) export class WorkspacesModule {} 분명 무언가가 다른것같은데 저는 아무리 찾아도 나오지 않습니다.ㅠㅠㅠㅠ 해결방법 키워드를 주시면 감사드리겠습니다.ㅠㅠㅠ
- 미해결Slack 클론 코딩[백엔드 with NestJS + TypeORM]
AuthGaurd, canActivate관련
안녕하세요 강의를 하며 passport 기능 따라구현하는 중에 막힌곳이 있어서 문의드립니다. 로그인 쪽 개발중 프론트엔드에서 요청을 하면 canActivate중에 문제가 있는지 401 에러를 뱉습니다. 가드가 컨트롤러 앞단에있는 거의 가장 처음 트래픽을 맞이하는 부분이라고 이해만 하고 canActivate에 대한 이해가 없어서 이부분에서 에러를 뱉어내서 조금 막막하네요 어떤 부분을 공부하거나 찾아봐야 할지 문의드립니다. @Injectable() export class LocalAuthGuard extends AuthGuard('local') { async canActivate(context: ExecutionContext): Promise<boolean> { console.log('가드'); //로그 정상적으로 찍힘 console.log(context); //로그 찍힘 const can = await super.canActivate(context); console.log(can); //이 로그가 찍히기 전에 401 에러뱉고 끝. 프론트 엔드 응답값은 {"success":false,"code":401,"data":"Unauthorized"} if (can) { const request = context.switchToHttp().getRequest(); console.log('login for cookie'); await super.logIn(request); } return true; } }
- 미해결Slack 클론 코딩[백엔드 with NestJS + TypeORM]
강의 순서 관련
Slack 클론 코딩 백엔드와 프론트 엔드 강의 두개 모두 구입해서 이제 공부해보려하는데 어떤 강의를 먼저 듣는것이 좋을까요?
- 미해결Slack 클론 코딩[백엔드 with NestJS + TypeORM]
SQL을 보고싶어요!
안녕하세요 제로초님! 강의 정말 너무너무 잘 보고있는 수강생 겸 팬입니다! 해당 강의에서 typeorm-model-generator를 통해 entity파일을 생성하는데, 생성된 파일을 보고 각 테이블의 속성을 파악할 순있지만, 실제로 DB에 테이블들을 생성할 때의 쿼리가 궁금합니다! 혹시 각 테이블 별 create문 DDL을 알려주실 수 있을까요? @Column("varchar", { name: "password", length: 100, select: false }) password: string; 와 같은 파일이 생성된다면 select속성은 직접 작성한 것인지, 아니면 mysql에서 테이블을 생성할 때 설정할 수 있는것인지?! unique 속성은 그냥 적으면 되는것인지, fk를 잡아야하는지 등등.. 아직 초보라ㅜㅜ 파일만 보고서는 한계가 있는 부분이 있어 자세히 공부하고 싶은 마음에 요청드립니다!!
- 미해결Slack 클론 코딩[백엔드 with NestJS + TypeORM]
SQL 쿼리문 성능 질문
선생님 sql 쿼리 순서가 join, where 순서라서 where보다 가능하다면 join으로 걸러주는게 성능상 더 좋다고 알고있습니다. 우선 위 생각이 맞는지 궁금하고 만약 아래 join문에서 모든 컬럼들이 인덱싱 되어 있다고 가정하고 SELECT COUNT(c.createdAt) AS unReads, c.RoomId AS RoomId FROM $chatTableName cLEFT JOIN $chatReadersTableName cr ON // 이렇게 특정한 고정값이 있다면 이것을 앞에 넣는게 성능상 좋을까요?cr.UserId = {특정한 고정값} AND cr.ChatRoomId = c.RoomId AND cr.ChatCreatedAt = c.createdAt // 아니면 이렇게 연계키로 매핑먼저 시켜준다음 걸러주는게 성능상 좋을까요?cr.ChatRoomId = c.RoomId AND cr.ChatCreatedAt = c.createdAt AND cr.UserId = {특정한 고정값} WHERE cr.ChatCreatedAt IS NULLGROUP BY c.RoomId; 둘의 결과가 똑같은 것 같은데 성능상 뭐가 더 좋을지 조언을 듣고싶어 질문드립니다!
- 미해결Slack 클론 코딩[백엔드 with NestJS + TypeORM]
질문드립니다 findOne 결과값이 왜 null이나오는지 잘모르겠네요
const num_auth = await this.emailsrepository.findOne({ where: { auth_num: authnum }, }); 이코드는 문제의 코드입니다 authnum = String 타입입니다. 콘솔로 num_auth를 찍으면 null 이 찍힙니다. 해당 데이터가 db에 실제 존재하고있습니다.. console.log로 authnum 이 잘들어오는것도 찍혔습니다. const findemail = await this.userRepository.findOne({ where: { email: email }, }); 근데 이코드는 이상하게 정확하게 조건절이 잘 먹히네요 왜 이러는걸까요 ?
- 미해결Slack 클론 코딩[백엔드 with NestJS + TypeORM]
ParseArrayPipe 질문드립니다.
ParseArrayPipe도 main에서 transform을 true로 설정하면 자동으로 변환해주나요?
- 해결됨Slack 클론 코딩[백엔드 with NestJS + TypeORM]
typeorm을 통해 db 생성을 하려고 하는데 에러가 발생합니다.
"start:dev": "nest build --webpack --webpackPath webpack-hmr.config.js --watch", 스크립트를 사용하여 실행했을 때 콘솔에 찍히는 에러입니다. // app.module.tsimport { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';import { ConfigModule, ConfigService } from '@nestjs/config';import { AppController } from './app.controller';import { AppService } from './app.service';import { LoggerMiddleware } from './middlewares/logger.middleware';import { UsersModule } from './users/users.module';import { WorkspacesModule } from './workspaces/workspaces.module';import { ChannelsModule } from './channels/channels.module';import { DmsModule } from './dms/dms.module';import { TypeOrmModule } from '@nestjs/typeorm';@Module({ imports: [ ConfigModule.forRoot({ isGlobal: true }), TypeOrmModule.forRoot({ type: 'mysql', host: 'localhost', port: 3306, username: process.env.DB_USERNAME, password: process.env.DB_PASSWORD, database: process.env.DB_DATABASE, autoLoadEntities: true, keepConnectionAlive: true, migrations: [__dirname + '/migrations/*.ts'], charset: 'utf8mb4', synchronize: true, logging: true, }), UsersModule, WorkspacesModule, ChannelsModule, DmsModule, ], controllers: [AppController], providers: [AppService, ConfigService],})export class AppModule implements NestModule { configure(consumer: MiddlewareConsumer): any { consumer.apply(LoggerMiddleware).forRoutes('*'); }} TypeOrm 모듈 관련 부분입니다. mysql에 스키마는 직접 만들어야 한다고 하셔서 만들은 화면입니다. typeORM 0.3.0 을 사용을 하고 있으며, ormconfig.ts 파일 대신 dataSource.ts 파일로 바꾼 상태지만, app.module.ts를 확인해보면 dataSource.ts 파일을 사용하지 않고 직접 설정 정보를 넣어준 상태입니다.에러코드 관련해서 검색을 해보니 webpack 문제라는 글을 보긴 했는데, 정말 webpack 때문에 발생한 오류인지, 그렇다면 어떻게 해결해야하는지 모르겠습니다.
- 미해결Slack 클론 코딩[백엔드 with NestJS + TypeORM]
트랜잭션 질문드립니다.
안녕하세요 . 강의 14:40 부분에서 queryRunner를 사용 시 중간에 트랜잭션으로 실행되어야하는 부분도 queryRunner.manger()... 을 통해 실행이 되어야 한다고 강의에 나와있습니다. 만약 아래와 같이 다른 함수를 호출한다면 트랜잭션이 정상적으로 실행이 되지 않는 것인지 궁금합니다. 트랜잭션 실행을 위해서는 모두 새롭게 함수를 작성하여야 하는 것이 맞는 것인가요? const queryRunner = getConnection().createQueryRunner(); await queryRunner.connect(); ... await this.createUser(createUserDto); await this.updateUserCount(updateUserCountDto); ... 함수는 임의로 작성하였습니다. 각 함수에는 await this.userRepository ... 로 구성되어 있습니다.
- 미해결Slack 클론 코딩[백엔드 with NestJS + TypeORM]
SQL Injection 질문드립니다!
안녕하세요. 12:40에 `${url}`을 사용시에는 sql 인젝션 공격에 취약하다고 하셨는데 파라미터로 넣는것과 어떤 차이가 있기에 취약하고, 파라미터로 넣으면 어떻게 공격을 방어해주는 것인지 궁금합니다! 그렇다면 만약 현재 `${url}` 형싱을 이용했다면 모든 코드를 파라미터 형태로 변경하는 것이 좋을까요?
- 미해결Slack 클론 코딩[백엔드 with NestJS + TypeORM]
옵셔널한 프로퍼티에서 ?의 사용
안녕하세요. 강의를 보고 프로젝트 진행중 질문사항이 있어서 문의드립니다.response와 request dto 분리해서 작업중인데 response dto에서 @ApiProperty({nullable:true}) @Expose() name?: string;이렇게 작성시 '?'를 작성할 필요가 없지않나 싶어서요!?가 값이 optional로 들어오지 않을 수 있다라는 느낌인데 이건 request dto에서만 작성하고 response dto에서는 작성안해도되는게 맞을까요?
- 해결됨Slack 클론 코딩[백엔드 with NestJS + TypeORM]
이전 질문에 연장되는 질문입니다.
안녕하세요 조현영님. 주말에도 답변 계속 달아주셔서 감사드립니다. 정말 도움이 많이됩니다! 저번 질문에서 제가 리뷰와 이미지 사이의 관계를 여쭤봤을 때 조현영님께서 리뷰와 이미지 사이의 관계는 일대다라고 하셨었죠. 이렇게 되면 리뷰쪽 컬럼에는 oneToMany, 이미지 쪽에는 ManyToOne이 붙게 되어 더불어 JoinColumn까지 이미지 쪽에 붙게 됩니다. 그러면 이미지 쪽에 리뷰에 관한 외래키 컬럼이 생성됩니다. 그런데 저는 여기서 위와 똑같은 방식으로 리뷰 대신 문의의 관련된 엔티티와 리뷰와 이미지를 관계 설정한것 처럼 똑같이 하려 합니다. 그러면 결국에 이미지 쪽에는 리뷰에 관한 외래키 컬럼과 문의에 관한 외래키 컬럼이 생기게 되는 것이죠. 여기서가 핵심인데 이렇게 되면 이미지 엔티티에는 리뷰, 문의가 공존하게 되는데 만약 리뷰가 있을시 문의의 외래키 컬럼에는 null값으로 존재하게 되고 문의가 있을시 리뷰의 외래키 컬럼에는 null값으로 존재합니다. 그리고 이미지에 추가적으로 상품 이미지 역시 존재합니다. 이러면 이미지 엔티티에 세 개(상품, 리뷰, 문의)의 외래키 컬럼이 존재하게 되는데 만약에 한 개의 컬럼에 값이 존재할 때 나머지 두개는 null값인 형태입니다. 그래서 제 결론은 이미지 엔티티 하나에 세 개의 외래키를 전부 넣는것이 아닌 예를 들어 상품 이미지 엔티티, 리뷰 이미지 엔티티, 문의 이미지 엔티티 이런식으로 엔티티를 각각 만들어야 할거 같은데 실무에서는 어떻게 해결하는지 조현영님의 의견이 궁금합니다.
- 해결됨Slack 클론 코딩[백엔드 with NestJS + TypeORM]
many to many 와 one to many, many to one에 대해서 질문드립니다.
안녕하세요 조현영님. 다대다 관계를 설정할 때 관계를 연결할 컬럼을 만들고 many to many 데코레이터를 달은 후 둘 중하나의 컬럼에다가 보통 joinTable컬럼을 달잖아요. 그런데 joinTable을 달게 되면 새로운 테이블이 하나 더 생기게 될텐데 제가 관계 설계중 상품과 리뷰의 관계를 many to many로 서로 해주려 했었는데 이러면 불필요한 테이블이 하나 생기게 되어 필요가 없을거 같아서요. 그런데 또 many to many를 one to many와 many to one으로 바꿔서 사용이 가능하다 하셨는데 질문1. 만약 joinTable대신 joinColumn을 쓰고 싶을 때 many to many 대신 one to many, many to one을 사용할 수 있을까요? 질문2. 위 질문이 가능하다면 저는 many to many를 쓰는 대신 상품 엔티티와 유저 엔티티 사이에 리뷰 엔티티를 만들어 놓고 상품 엔티티 - one to many -> 리뷰 엔티티 상품 엔티티 <- many to one - 리뷰 엔티티 유저 - one to many -> 리뷰 엔티티 유저 <- many to one - 리뷰 엔티티 이런식으로 사용이 가능할까요?