훌륭한 Nest.js 블로그 만들기: 태그로 필터링하기
Min-jun Kim
Dev Intern · Leapcell

이전 튜토리얼에서, 블로그 태그를 생성하고 표시하는 기능을 추가했습니다.
다음으로, 태그 기능의 나머지 부분인 태그별 게시물 필터링을 완료할 것입니다.
사용자가 게시물 상세 페이지에서 태그 링크를 클릭하면, 해당 태그의 모든 게시물만 표시하는 목록 페이지로 이동해야 합니다. 이를 위해 백엔드에 새로운 라우트와 처리 로직을 생성하고, 프론트엔드에 해당 뷰를 만들어야 합니다.
1단계: 서비스 로직 확장
먼저, 태그 ID별로 게시물을 찾기 위한 새 메서드를 서비스에 추가합니다.
src/posts/posts.service.ts
파일을 열고 다음 내용을 추가합니다:
// src/posts/posts.service.ts // ... (다른 import 및 생성자) async findByTag(tagId: string): Promise<Post[]> { return this.postsRepository .createQueryBuilder('post') .leftJoinAndSelect('post.tags', 'tag') .where('tag.id = :tagId', { tagId }) .orderBy('post.createdAt', 'DESC') .getMany(); } // ... (파일의 나머지 부분)
findByTag
는 다대다 관계를 포함하므로createQueryBuilder
를 사용하여 게시물과 태그의 중간 테이블을 JOIN하고tagId
로 필터링합니다.
필터 페이지에 태그 이름을 표시하려면 TagsService
에 간단한 findOne
메서드를 추가해야 합니다.
// src/tags/tags.service.ts async findOne(id: string): Promise<Tag | null> { return this.tagsRepository.findOneBy({ id }); }
2단계: 컨트롤러 및 라우트 생성
이제 /tags/:id
요청을 처리할 컨트롤러를 구현합니다.
컨트롤러 로직 작성
해당 컨트롤러 파일을 생성합니다:
nest generate controller tags
src/tags/tags.controller.ts
파일을 열고 다음 코드를 추가합니다:
// src/tags/tags.controller.ts import { Controller, Get, Param, Render, Request } from '@nestjs/common'; import { TagsService } from './tags.service'; import { PostsService } from '../posts/posts.service'; @Controller('tags') export class TagsController { constructor(private readonly tagsService: TagsService, private readonly postsService: PostsService) {} @Get(':id') @Render('posts-by-filter') async findPostsByTag(@Param('id') id: string, @Request() req) { const posts = await this.postsService.findByTag(id); const tag = await this.tagsService.findOne(id); return { posts, user: req.session.user, filterType: 'Tag', filterName: tag ? tag.name : 'Unknown', }; } }
모듈 종속성 업데이트
위 코드가 올바르게 작동하려면 서비스 간의 종속성을 처리하기 위해 모듈 파일을 업데이트해야 합니다.
PostsService
와 TagsService
는 서로 의존하므로 순환 종속성 문제를 해결하기 위해 forwardRef
를 사용해야 합니다.
먼저, 다른 모듈에서 사용할 수 있도록 PostsService
를 내보냅니다. 동시에 PostsModule
이 TagsModule
을 참조하는 방식을 수정합니다.
// src/posts/posts.module.ts import { Module, forwardRef } from '@nestjs/common'; // ... @Module({ imports: [ TypeOrmModule.forFeature([Post]), CommentsModule, TrackingModule, forwardRef(() => TagsModule), // 순환 종속성 방지를 위해 forwardRef 사용 ], // ..., exports: [PostsService], // PostsService 내보내기 }) export class PostsModule {}
그런 다음 TagsModule
에 PostsModule
을 가져옵니다.
// src/tags/tags.module.ts import { Module, forwardRef } from '@nestjs/common'; import { PostsModule } from '../posts/posts.module'; // import //... @Module({ imports: [TypeOrmModule.forFeature([Tag]), forwardRef(() => PostsModule)], // imports에 추가 controllers: [TagsController], providers: [TagsService], }) export class TagsModule {}
3단계: 프론트엔드 뷰 생성
마지막으로, views
폴더에 posts-by-filter.ejs
뷰 파일을 생성합니다. 이 파일은 태그별로 필터링된 게시물 목록을 표시하는 데 사용됩니다. 내용은 index.ejs
와 매우 유사합니다.
views/posts-by-filter.ejs
파일을 생성합니다:
<%- include('_header', { title: `Posts in ${filterName}` }) %> <div class="filter-header"> <h2>Posts in Tag: <strong><%= filterName %></strong></h2> </div> <% if (posts.length > 0) { %> <div class="post-list"> <% posts.forEach(post => { %> <article class="post-item"> <h2><a href="/posts/<%= post.id %>"><%= post.title %></a></h2> <p><%= post.content.substring(0, 150) %>...</p> <small><%= new Date(post.createdAt).toLocaleDateString() %></small> </article> <% }) %></div> <% } else { %> <p>No posts found in this tag.</p> <% } %> <a href="/" class="back-link" style="margin-top: 2rem;">← Back to Home</a> <%- include('_footer') %>
이 템플릿은 동적으로 제목 (예: "Posts in Tag: Tutorial")과 게시물 목록을 표시합니다. 태그 아래 게시물이 없으면 메시지를 표시합니다.
이제 전체 필터링 프로세스가 완료되었습니다. 애플리케이션을 다시 시작하고 게시물 상세 페이지의 태그 링크를 클릭하면 해당 필터 페이지로 이동합니다.
실행 및 테스트
애플리케이션을 다시 시작합니다:
npm run start:dev
브라우저를 열고 다음으로 이동합니다: http://localhost:3000/
태그가 포함된 게시물을 방문하고 아무 태그나 클릭합니다.
해당 태그의 필터 페이지로 리디렉션되어 해당 태그에 속한 모든 게시물 목록을 볼 수 있습니다.
이 두 튜토리얼을 통해 블로그에 완전한 태그 시스템을 추가했습니다.
이 시점에서 블로그 프로젝트는 기본 아키텍처부터 핵심 기능, 콘텐츠 구성, 데이터 분석까지 모든 것을 다루었습니다.
블로그의 상세 기능은 무궁무진합니다. 현재 프레임워크를 기반으로 더 많은 기능을 계속 추가할 수 있습니다. 나머지는 여러분의 상상력에 달려 있습니다!
이전 튜토리얼: