Community Lounge는 네온 테마와 글라스모피즘 디자인을 적용한 커뮤니티 게시판 웹 애플리케이션입니다.
Next.js App Router와 TypeScript를 기반으로 화면과 라우팅을 구성하고, Supabase를 사용해 인증, PostgreSQL 데이터베이스, 이미지 스토리지 기능을 연결했습니다.
인증 상태에 따라 접근 가능한 화면과 액션을 분리하고, 게시글과 댓글 데이터의 작성자 권한을 고려해 수정/삭제 흐름을 구성했습니다.
Supabase 마이그레이션으로 데이터베이스 스키마를 관리하고, Vercel 배포 환경에서 필요한 공개 환경변수를 분리해 웹 프로젝트 배포 흐름을 정리했습니다.
문제: 게시글 작성은 성공하고 DB에도 데이터가 저장되는데, 목록 화면에서는 계속 "아직 게시글이 없습니다"처럼 보였습니다.
원인: 목록 조회 쿼리에서 Supabase 자동 조인 관계를 잘못 가정했습니다. 실제 스키마는 posts와 profiles가 직접 외래키로 연결된 구조가 아니라, 둘 다 auth.users를 바라보는 구조였습니다.
.select(`
*,
profiles:user_id (nickname),
comments(count)
`)
위 쿼리는 Supabase에게 posts.user_id를 기준으로 profiles와 자동 조인하라고 요청합니다. 하지만 실제 관계는 posts.user_id -> auth.users.id, profiles.id -> auth.users.id라서 posts에서 profiles로 직접 자동 조인을 할 수 없었습니다.
게다가 기존 코드는 조회 에러를 빈 배열로 바꿔 반환하고 있어서, 실제 에러가 화면에 드러나지 않고 게시글이 없는 것처럼 보였습니다.
if (error) {
return { error: error.message, posts: [] };
}
해결은 게시글을 먼저 조회한 뒤, 게시글의 user_id 목록을 모아 profiles 테이블을 별도로 조회하고, 코드에서 작성자 정보를 합치는 방식으로 변경했습니다. 이 과정에서 DB에는 데이터가 정상 저장되는지, 조회 쿼리에서 어떤 관계가 실패하는지 단계별로 분리해서 확인했습니다.
이 경험을 통해 Supabase의 자동 조인은 테이블 이름이 아니라 실제 외래키 관계를 기준으로 동작한다는 점과, 에러를 빈 상태로 숨기면 디버깅이 어려워진다는 점을 배웠습니다.