nest-morgan 패키지 사용
미해결
Slack 클론 코딩[백엔드 with NestJS + TypeORM]
안녕하세요! 강의 마지막 부분에 nest-morgan 패키지를 사용하면 된다고 하셨는데요. npm 문서를 확인해보니 해당 패키지가 deprecated 되어 있다고 명시돼 있습니다. 혹시 여전히 해당 패키지를 사용하고 계신지 혹은 다른 로그 패키지를 사용하고 계신지 궁금합니다!
- node.js
- express
- nestjs
- typeorm
172만명의 커뮤니티!! 함께 토론해봐요.
미해결
Slack 클론 코딩[백엔드 with NestJS + TypeORM]
안녕하세요! 강의 마지막 부분에 nest-morgan 패키지를 사용하면 된다고 하셨는데요. npm 문서를 확인해보니 해당 패키지가 deprecated 되어 있다고 명시돼 있습니다. 혹시 여전히 해당 패키지를 사용하고 계신지 혹은 다른 로그 패키지를 사용하고 계신지 궁금합니다!
미해결
실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요. 1. 강의 내용과 관련된 질문을 남겨주세요. 2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요. (자주 하는 질문 링크: https://bit.ly/3fX6ygx) 3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요. (질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG) 질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요. ========================================= [질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예/아니오) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오) [질문 내용] plugins { id 'org.springframework.boot' version '2.6.5' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' id "com.ewerk.gradle.plugins.querydsl" version "1.0.10" } group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '11' ext["hibernate.version"] = " 5.6.5.Final " configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'javax.validation:validation-api' implementation 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate5' //JdbcTemplate 추가 //implementation 'org.springframework.boot:spring-boot-starter-jdbc' //MyBatis 추가 implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.0' //JPA, 스프링 데이터 JPA 추가 implementation 'org.springframework.boot:spring-boot-starter-data-jpa' //Querydsl 추가 implementation 'com.querydsl:querydsl-jpa' annotationProcessor "com.querydsl:querydsl-apt:$ { dependencyManagement.importedProperties['querydsl.version'] } :jpa" annotationProcessor "jakarta.annotation:jakarta.annotation-api" annotationProcessor "jakarta.persistence:jakarta.persistence-api" //H2 데이터베이스 추가 runtimeOnly 'com.h2database:h2' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' //테스트에서 lombok 사용 testCompileOnly 'org.projectlombok:lombok' testAnnotationProcessor 'org.projectlombok:lombok' } tasks.named('test') { useJUnitPlatform() } //querydsl 추가 //def querydslDir = '$buildDir/generated' def querydslDir = "src/main/generated" //clean { // delete file(generated) //} querydsl { library = "com.querydsl:querydsl-apt" jpa = true querydslSourcesDir = querydslDir } sourceSets { main { java { srcDirs = ['src/main/java', querydslDir] } } } compileQuerydsl { options.annotationProcessorPath = configurations.querydsl } configurations { querydsl.extendsFrom compileClasspath } 강사님이 올려주신 내용 + 구글링 + 직접 수정했습니다. 간혹, 빌드는 되는데 generated 파일에 QClass 안보이시는 분들은 queryDsl 저장되는 경로에 $buildDir 대신 'src/main/'으로 해보세요
미해결
탄탄한 백엔드 NestJS, 기초부터 심화까지
포스트맨으로 정확히 헤더에 { Authorization: 토큰값 } 넣고 { "success": false, "timestamp": "2023-06-19T07:07:40.995Z", "path": "/cats", "statusCode": 401, "message": "Unauthorized" } 으로 나와서 response를 콘솔로 찍어보니 authorization과 같이 a가 소문자로 변환되어 찍혔는데요, 혹시 이와 관련되서 토큰값을 제대로 못받는걸까요 ? 혹시나해서 코드는 깃허브 참고해서 비교해봤는데 도저히 못찾겠어서 도움요청드립니다 ㅜ
해결됨
[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
안녕하세요? 강의 잘 따라가고 있습니다. 강의진행할때마다 지난 강의폴더를 복사 후 이름을 바꾸는 식으로 진행을 하는데 좀 문제가 생겨서 질문드립니다. 폴더를 복사후 생성(~~~.copy)까지는 문제가 없는데 폴더이름을 변경하는 순간 아래 그림과 같이 프리티어관련 메시지가 나온 후 nestjs/graphql 모듈을 읽어오지 못하는 문제가 발생합니다. 일단 node_modules와 yarn.lock폴더를 삭제후 yarn install을 하면 정상적으로 읽어오긴 하는데 매번 그렇게 하니 번거롭네요.. 뭐가 문제인걸까요?
미해결
실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발
주문 서비스 개발 부분 강의를 듣다가 궁금한 점이 있어 질문드립니다. 주문 취소(OrderService.cancelOrder) 부분에서order.cancel() 만 작성하면 JPA 특징으로 인해, 데이터 업데이트 관련 쿼리 로직 없이 자동으로 업데이트 된다고 하셨는데요. 주문 생성(OrderService.order)의 EntityManager 가 사용된 orderRepository.save (order); 와 혼동이 와서 질문 남깁니다. 주문 취소 order.cancel() 메서드를 타면서 엔티티의 값(status, count 등) 변경 내역이 자동으로 업데이트 되는 것이라고 하면, 주문 생성도 Order.createOrder(member, delivery, orderItem); 를 타면서 엔티티의 값들을 설정하기 때문에 orderRepository.save (order); 를 사용하지 않아도 되는게 아닌가 라는 생각이 듭니다. 제가 JPA 를 잘 몰라서 혼란이 오는 것 같은데.. 쿼리는 직접 작성하지 않아도 업데이트/저장/삭제 등 DB 조작이 일어나려면 EntityManager 를 통해야 된다고 생각하고 있었습니다. 주문 취소는 EntityManager 없이 업데이트 되고, 주문 생성은 EntityManager의 persist() 를 사용해서 저장하는 것 이 2가지 차이점에 대해서 설명 부탁드립니다! 감사합니다 :)
해결됨
[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
과제30 구글 로그인 과제 수행중 자꾸 이 에러 때문에... 정답을 찾지 못하였습니다..무엇이 문제인지 모르겠어요...
해결됨
[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
혹시 MYSQL만 사용하는 경우랑 graphql이랑 같이하는경우랑 TypeORM사용법 차이 많이 나나요?
미해결
Slack 클론 코딩[백엔드 with NestJS + TypeORM]
안녕하세요~ 제가 현재 Nestjs 와 React로 구성한 app애서 추가로 다른 app의 API를 사용해야되는 상황인데요. API키와SECRET을 생성하고 이를 이용하여 호출하고 있는데, 이 때 클라이언트에서 요청을 한 내용을 보면 이 Authentication 관련된 토큰들이 다 노출되서 고민입니다. 정확히 말하면 이건 백엔드 관련도 아니라 프론트쪽이긴한데요.. 프론트에서 다른 앱의 Restful API를 사용하여 요청할때 사용되는 토큰을 보안적으로 안전하게 숨길 수 있는 방법이 있을까요?
해결됨
[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
quiz19 -2 에서 토큰기반인증(로그인)된 유저의 비밀번호 변경 로직을 구현하고 있는데요.. resolver와 service의 연결부분에서 에러가 발생하는데 혼자서 해결이 어려워 문의드립니다. 제가 생각한 updateUserPwd의 로직은 유저가 있는지의 여부 확인 비밀번호의 일치 여부 확인 bcrypt로 변경하고자하는 비밀번호의 암호화 암호화된 변경비밀번호를 해당하는 email의 DB에 저장 이렇게 하면 끝나는 로직이라고 생각하고 소스코드를 작성했습니다. 1차적으로 제가 생각한 로직에 빠진 부분이 있는지 궁금하고 지금의 소스코드로 어떤부분을 보완해야하는지 궁금합니다.(현재 코드블록으로 공유한 내용은 users.service.ts에서 return부분에 where에서 에러가 발생하는 상황입니다..) 추가로 필요한 정보나 내용, 소스코드가 있으면 추가적으로 공유하도록 하겠습니다. 도와주세요~~~ 나머지 import해온 class들은 수업을 통해서 그대로 가져온 내용들입니다. //users.resolver.ts @UseGuards(gqlAuthAccessToken) @Mutation(() => String) updateUserPwd( @Args('email') email: string, @Args('password') password: string, ): string { this.usersService.updateUserPwd({ email, password }); return '비밀번호 수정 성공'; } //users.service.ts async updateUserPwd({ email, password, }: IUserServiceUpdateUserPwd): Promise<UpdateResult> { const user = await this.findOneByEmail({ email }); if (!user) throw new UnprocessableEntityException('등록되지 않은 이메일 입니다.'); const isAuth = await bcrypt.compare(password, user.password); if (!isAuth) throw new UnprocessableEntityException('틀린 암호입니다.'); const hashedPassword = await bcrypt.hash( password, Number(process.env.SALT), ); return this.UsersRepository.update( { password: hashedPassword }, { where: { email: user.email }, }, ); }
해결됨
Slack 클론 코딩[백엔드 with NestJS + TypeORM]
파라미터로 id나 URL 등 값 받아올 때와 쿼리스트링으로 받아올 때 파라미터로도 perPage나 page 등 받아올 수 있는데 어떤 차이로 파라미터와 쿼리스트링을 나누어서 사용해야 할지 질문드립니다. 단순히 파라미터는 http 요청시 한 번 정해지면 고정되는 값에 사용하고 쿼리스트링은 매 요청마다 변하는 값에 사용한다고 생각이 드는데 맞을까요??
미해결
타입스크립트의 모든 것
안녕하세요 정말 좋은 강의 감사합니다. 혹시 vscode extension은 어떤거 쓰시는지 알수있을까요..?
해결됨
[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
도커 빌드하고 실행하는 과정에서 에러가 발생하네요.. 답변해주신 다른 글보고 해봐도 잘안되네요.. 일단 빌드시 생기는 에러입니다. yarn install 할때 경고가 엄청 발생하는데 apollo-server-express가 문제인 것 같은데 정확한건 잘 모르겠네요.. 사용한 버전정보입니다. 도커 빌드가 완료되서 실행을 해도 gateway가 다운되고 1번에러라는 메세지만 받네요 .. 어떻게 해야 할 지 답변부탁드립니다 ㅜㅜ
해결됨
Slack 클론 코딩[백엔드 with NestJS + TypeORM]
안녕하세요 이장우 닮은 제로초 선생님, // workspaces.service.ts async createWorkspaceMembers(url: string, email: string) { const workspace = await this.workspacesRepository.findOne({ where: { url }, relations: ['Channels'], }); // queryBuilder 버전 this.workspacesRepository .createQueryBuilder('workspaces') // typeorm은 innerJoin 하면 join한 테이블은 가져오지 않는데, // innerJoinAndSelect을 하면 join한 테이블을 다 가져온다. .innerJoinAndSelect('workspaces.Channels', 'channels') .getOne(); const user = await this.usersRepository.findOne({ where: { email } }); if (!user) return null; const workspaceMember = new WorkspaceMembers(); workspaceMember.WorkspaceId = workspace.id; workspaceMember.UserId = user.id; await this.workspaceMembersRepository.save(workspaceMember); const channelMember = new ChannelMembers(); channelMember.ChannelId = workspace.Channels.find( (v) => v.name === '일반', ).id; channelMember.UserId = user.id; await this.channelMembersRepository.save(channelMember); } 강의에서 위와 같이 createWorkspaceMembers 를 작성하셨는데, 여기도 transaction을 걸어주는게 낫겠죠? 걸어주는게 맞다면, 아래와 같이 하면 되나요? async createWorkspaceMembers(url: string, email: string) { const queryRunner = this.dataSource.createQueryRunner(); await queryRunner.connect(); await queryRunner.startTransaction(); try { const workspace = await queryRunner.manager.findOne(Workspaces, { where: { url }, relations: ['Channels'], }); const user = await queryRunner.manager.findOne(Users, { where: { email } }); if (!user) return null; const workspaceMember = new WorkspaceMembers(); workspaceMember.WorkspaceId = workspace.id; workspaceMember.UserId = user.id; await queryRunner.manager.save(workspaceMember); const channelMember = new ChannelMembers(); channelMember.ChannelId = workspace.Channels.find( (v) => v.name === '일반', ).id; channelMember.UserId = user.id; await queryRunner.manager.save(channelMember); await queryRunner.commitTransaction(); } catch (error) { console.error(error); await queryRunner.rollbackTransaction(); } finally { await queryRunner.release(); } }
해결됨
실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
[질문 템플릿] 1. 강의 내용과 관련된 질문인가요? (예) 2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예) 3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예) [질문 내용] 현재 스프링 부트 3.1 로 강의를 따라가고 있습니다. default_batch_fetch_size:10 을 설정해서 나가는 쿼리가 줄어드는 것을 확인했습니다만, 쿼리가 두 개가 아니고 총 세 개가 나왔습니다. 023-06-13T23:24:02.666+09:00 DEBUG 68750 --- [nio-8080-exec-5] org.hibernate.SQL : select o1_0.order_id, d1_0.delivery_id, d1_0.city, d1_0.street, d1_0.zipcode, d1_0.status, m1_0.memeber_id, m1_0.city, m1_0.street, m1_0.zipcode, m1_0.name, o1_0.order_date, o1_0.status from orders o1_0 join member m1_0 on m1_0.memeber_id=o1_0.member_id join delivery d1_0 on d1_0.delivery_id=o1_0.delivery_id offset ? rows fetch first ? rows only 2023-06-13T23:24:02.676+09:00 INFO 68750 --- [nio-8080-exec-5] p6spy : #1686666242676 | took 2ms | statement | connection 8| url jdbc:h2:tcp://localhost/~/springbootjpa select o1_0.order_id,d1_0.delivery_id,d1_0.city,d1_0.street,d1_0.zipcode,d1_0.status,m1_0.memeber_id,m1_0.city,m1_0.street,m1_0.zipcode,m1_0.name,o1_0.order_date,o1_0.status from orders o1_0 join member m1_0 on m1_0.memeber_id=o1_0.member_id join delivery d1_0 on d1_0.delivery_id=o1_0.delivery_id offset ? rows fetch first ? rows only select o1_0.order_id,d1_0.delivery_id,d1_0.city,d1_0.street,d1_0.zipcode,d1_0.status,m1_0.memeber_id,m1_0.city,m1_0.street,m1_0.zipcode,m1_0.name,o1_0.order_date,o1_0.status from orders o1_0 join member m1_0 on m1_0.memeber_id=o1_0.member_id join delivery d1_0 on d1_0.delivery_id=o1_0.delivery_id offset 0 rows fetch first 100 rows only; 2023-06-13T23:24:02.681+09:00 DEBUG 68750 --- [nio-8080-exec-5] org.hibernate.SQL : select o1_0.order_id, o1_0.order_item_id, o1_0.count, o1_0.item_id, o1_0.order_price from order_item o1_0 where array_contains(?,o1_0.order_id) 2023-06-13T23:24:02.689+09:00 INFO 68750 --- [nio-8080-exec-5] p6spy : #1686666242689 | took 0ms | statement | connection 8| url jdbc:h2:tcp://localhost/~/springbootjpa select o1_0.order_id,o1_0.order_item_id,o1_0.count,o1_0.item_id,o1_0.order_price from order_item o1_0 where array_contains(?,o1_0.order_id) select o1_0.order_id,o1_0.order_item_id,o1_0.count,o1_0.item_id,o1_0.order_price from order_item o1_0 where array_contains('ar2: ARRAY [CAST(1 AS BIGINT), CAST(2 AS BIGINT), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL]',o1_0.order_id); 2023-06-13T23:24:02.690+09:00 DEBUG 68750 --- [nio-8080-exec-5] org.hibernate.SQL : select i1_0.item_id, i1_0.dtype, i1_0.category_item_id, i1_0.name, i1_0.price, i1_0.stock_quantity, i1_0.author, i1_0.isbn, i1_0.artist, i1_0.etc from item i1_0 where array_contains(?,i1_0.item_id) 2023-06-13T23:24:02.691+09:00 INFO 68750 --- [nio-8080-exec-5] p6spy : #1686666242691 | took 0ms | statement | connection 8| url jdbc:h2:tcp://localhost/~/springbootjpa select i1_0.item_id,i1_0.dtype,i1_0.category_item_id,i1_0.name,i1_0.price,i1_0.stock_quantity,i1_0.author,i1_0.isbn,i1_0.artist,i1_0.etc from item i1_0 where array_contains(?,i1_0.item_id) select i1_0.item_id,i1_0.dtype,i1_0.category_item_id,i1_0.name,i1_0.price,i1_0.stock_quantity,i1_0.author,i1_0.isbn,i1_0.artist,i1_0.etc from item i1_0 where array_contains('ar3: ARRAY [CAST(1 AS BIGINT), CAST(2 AS BIGINT), CAST(3 AS BIGINT), CAST(4 AS BIGINT), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL]',i1_0.item_id); 수강 중에 제가 따라 작성한 코드를 스프링부트 2.4.1에 그대로 옮겨보니 강의 내용대로 where in 쿼리가 나오는 것을 확인했습니다. 스프링부트 버전 차이에서 나오는 쿼리가 달라진 것 같은데, 제 생각이 맞을까요? 답변 확인 : 빠르게 답변해주셔서 감사합니다!! 덕분에 array_contains 로 최적화된 이유를 쉽게 이해할 수 있었습니다. (Hibernate에서 최적화를 했었었군요!) 아 그리고 강의 내용에서 쿼리 세 개 나온다는 것을 확인했습니다(잘못 본 것에 대해 질문글에서 수정을 해놨어야 했는데 빼놓지 않았었네요 ㅠㅠ) 다시 한번 상세한 답변 정말 감사드립니다.
해결됨
[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
회원가입 피그마 정답이 있는지 궁금하네요 열심히 만들었는데 비교를 할 수가 없어서 문제이네요 최대한 비슷하게 했는데 미묘하게 달라서 제가 맞게 한건지 모르겠네요 섹션 7에 파이널 정답이 있는건 아는데 섹션2랑은 많이 달라서 문제네요
해결됨
[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
안녕하세요! 오류내용 관련해서 질문사항이 있습니다. 새로운 프로젝트로 nestjs를 생성해서 회원가입 create API를 생성하려할때 graphql연결 부문에 있어서 위 이미지와 같이 에러가 발생합니다. 위 에러가 GraphQLError: Query root type must be provided.내용에 회원정보를 create하는 root type '@Mutation()'이 있는데도 오류가 나는데 여기서 '@Query()' 를 임의로 만들어 코드를 만들어 놓으면 정상적으로 연결이 완료가 되더라구요.. 조회역할을 하는 Query가 있어야 정상적으로 연결이 되는걸까요? 원리를 알고 싶습니다! ⬇️ @Query() 가 비활성화 됐을때 이미지(graphql 에러발생) ⬇️ @Query()가 활성화 됐을때 이미지(연결 정상)
미해결
탄탄한 백엔드 NestJS, 기초부터 심화까지
package-lock.js package.json tsconfig.json src디렉터리 강의 보면 다운받아서 초기설정해주는것같은데 얘네는 개발자가 원래 다 직접 쳐야하나요??? 아니면 매번 파일들을 저장했다가 프로젝트처음할때 붙여놓고 npm i 하고 해야하는건가요???
해결됨
[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
안녕하세요. 프로시저를 만든는 도중 에러가 발생해서 질문드립니다. 똑같이 작성했는데도 에러가 나오네요. 세미콜론도 여러번 확인했는데 혹시 오류가 나는 다른 이유가 있을까요??
해결됨
[코드캠프] 부트캠프에서 만든 고농축 백엔드 코스
안녕하세요 섹션27 08-04 nest.js-mysql 강의 듣다가 에러가 나서 질문드립니다. ERROR [TypeOrmModule] Unable to connect to the database. Retrying (1)... Error: connect ECONNREFUSED ::1:3306 위와 같이 에러가 나는데 계속 구글링해서 해결해보려고 했지만 고쳐지지가 않네요.. 시도해본것들: 일단 DBeaver에는 문제없이 연결됐지만 그래도 혹시나 해서 allowPubicKeyRetrieval를 true로 바꿔보고 UseSSL을 false로 해봤습니다. 비밀번호를 변경해보았습니다. localhost를 127.0.0.1으로 바꿔봤습니다. mysql 재실행 해봤습니다. entities경로 확인해봤습니다. -- 전부 안되네요 ㅠ 뭐가 문제인걸까요? 추가로 맥북시스템설정에 mysql이 없어서 (맥북 버전문제?) 터미널로 계속 실행시켜주고 있는데 다른 방법없을까요?
미해결
실전! Querydsl
안녕하세요. where 조건을 동적으로 생성해주는 부분에 대해서는 충분히 이해가 되었습니다. 그런데 혹시 특정 condition 값에 따라 join을 해줘야하는 테이블이 다른 경우라면 어떻게 처리되어야 할지 모르겠어서 글을 남기게 되었습니다. condition조건에 teamName이 있을 때만 team을 조인해주고 싶은데 join 구문을 동적으로 처리할 수 있는 좋은 방법이 어떤 것인지 질문드립니다. 😭 감사합니다.