본문 바로가기
IT/angular

Angular 17과 Supabase로 구축하는 실시간 채팅 애플리케이션

by higold 2024. 11. 1.
728x90

Angular 17과 Supabase로 구축하는 실시간 채팅 애플리케이션 완벽 가이드

목차

  1. 소개
  2. 기술 스택 소개
  3. 프로젝트 설정 및 준비
  4. 핵심 기능 구현
  5. 보안 및 인증
  6. 데이터베이스 설계
  7. UI/UX 구현
  8. 배포 프로세스
  9. 성능 최적화
  10. 결론 및 향후 발전 방향

소개

현대 웹 애플리케이션 개발에서 실시간 통신 기능은 필수적인 요소가 되었습니다. 특히 채팅 애플리케이션은 사용자 간의 즉각적인 상호작용을 가능하게 하는 중요한 기능입니다. 이 글에서는 Angular 17과 Supabase를 활용하여 전문적인 실시간 채팅 애플리케이션을 구축하는 방법을 상세히 알아보겠습니다.

"효과적인 실시간 통신은 현대 웹 애플리케이션의 핵심입니다. Angular 17과 Supabase의 조합은 이를 구현하는 최적의 솔루션을 제공합니다."

기술 스택 소개

프론트엔드 기술

  1. Angular 17
    • 최신 제어 흐름 구문
    • 독립형 컴포넌트
    • 신호(Signals) 시스템
    • 향상된 성능과 개발자 경험
  2. UI 프레임워크
    • Bootstrap 5
    • 반응형 디자인
    • 모던한 UI 컴포넌트

백엔드 기술

  1. Supabase
    • PostgreSQL 데이터베이스
    • 실시간 구독 기능
    • 강력한 인증 시스템
    • 확장 가능한 인프라
  2. 인증 시스템
    • Google OAuth 2.0
    • JWT 토큰 관리
    • 보안 정책 설정

프로젝트 설정 및 준비

초기 설정

# Angular 프로젝트 생성
ng new chat-application --style=scss --routing=true

# 필요한 의존성 설치
npm install @supabase/supabase-js
npm install bootstrap@5

환경 변수 설정

// environment.ts
export const environment = {
  production: false,
  supabaseUrl: 'YOUR_SUPABASE_URL',
  supabaseKey: 'YOUR_SUPABASE_KEY',
  googleClientId: 'YOUR_GOOGLE_CLIENT_ID'
};

핵심 기능 구현

1. 실시간 메시지 처리

실시간 메시지 처리는 Supabase의 구독 시스템을 활용하여 구현합니다.

// message.service.ts
export class MessageService {
  private messageSubscription: RealtimeSubscription;

  constructor(private supabase: SupabaseService) {
    this.setupMessageSubscription();
  }

  private setupMessageSubscription() {
    this.messageSubscription = this.supabase.client
      .from('messages')
      .on('INSERT', payload => {
        this.handleNewMessage(payload.new);
      })
      .on('DELETE', payload => {
        this.handleDeletedMessage(payload.old);
      })
      .subscribe();
  }

  async sendMessage(content: string) {
    const user = await this.supabase.getCurrentUser();
    return this.supabase.client
      .from('messages')
      .insert([
        {
          content,
          user_id: user.id,
          created_at: new Date()
        }
      ]);
  }
}

2. 사용자 인증 구현

Google OAuth를 통한 인증 시스템 구현:

// auth.service.ts
export class AuthService {
  private authState = signal<User | null>(null);

  async signInWithGoogle() {
    try {
      const { data, error } = await this.supabase.auth.signInWithGoogle();
      if (error) throw error;
      this.authState.set(data.user);
      return data;
    } catch (error) {
      console.error('Error signing in with Google:', error);
      throw error;
    }
  }
}

3. 메시지 삭제 기능

사용자 권한을 확인하고 메시지를 삭제하는 기능:

async deleteMessage(messageId: string) {
  const user = await this.getCurrentUser();
  const message = await this.getMessage(messageId);

  if (message.user_id !== user.id) {
    throw new Error('Unauthorized to delete this message');
  }

  return this.supabase.client
    .from('messages')
    .delete()
    .match({ id: messageId });
}

보안 및 인증

Row Level Security (RLS) 정책

Supabase에서 데이터 보안을 강화하기 위한 RLS 정책 설정:

-- messages 테이블에 대한 RLS 정책
CREATE POLICY "Users can insert their own messages"
ON messages FOR INSERT
WITH CHECK (auth.uid() = user_id);

CREATE POLICY "Users can view all messages"
ON messages FOR SELECT
TO authenticated
USING (true);

CREATE POLICY "Users can delete their own messages"
ON messages FOR DELETE
USING (auth.uid() = user_id);

데이터베이스 설계

테이블 구조

-- 사용자 테이블
CREATE TABLE users (
  id UUID REFERENCES auth.users PRIMARY KEY,
  username TEXT UNIQUE,
  avatar_url TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- 메시지 테이블
CREATE TABLE messages (
  id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
  content TEXT NOT NULL,
  user_id UUID REFERENCES users(id),
  created_at TIMESTAMPTZ DEFAULT NOW(),
  updated_at TIMESTAMPTZ DEFAULT NOW()
);

UI/UX 구현

채팅 인터페이스 컴포넌트

@Component({
  selector: 'app-chat',
  template: `
    <div class="chat-container">
      <div class="message-list" #messageList>
        @for (message of messages(); track message.id) {
          <div class="message" [class.own-message]="isOwnMessage(message)">
            <div class="message-header">
              <img [src]="message.user.avatar_url" class="avatar">
              <span class="username">{{ message.user.username }}</span>
              <span class="timestamp">{{ message.created_at | date:'short' }}</span>
            </div>
            <div class="message-content">
              {{ message.content }}
            </div>
            @if (isOwnMessage(message)) {
              <button class="delete-btn" (click)="deleteMessage(message.id)">
                Delete
              </button>
            }
          </div>
        }
      </div>
      <div class="message-input">
        <input 
          type="text" 
          [(ngModel)]="newMessage" 
          (keyup.enter)="sendMessage()"
          placeholder="Type a message..."
        >
        <button (click)="sendMessage()">Send</button>
      </div>
    </div>
  `
})

배포 프로세스

Vercel 배포 설정

  1. GitHub 저장소 연동
  2. 환경 변수 설정
    • SUPABASE_URL
    • SUPABASE_KEY
    • GOOGLE_CLIENT_ID
  3. 빌드 설정
    • Node.js 버전 지정
    • 빌드 명령어 설정
  4. 도메인 설정

성능 최적화

1. 메시지 페이지네이션

async getMessages(limit: number = 20, offset: number = 0) {
  return this.supabase.client
    .from('messages')
    .select(`
      id,
      content,
      created_at,
      user:users(id, username, avatar_url)
    `)
    .order('created_at', { ascending: false })
    .range(offset, offset + limit - 1);
}

2. 이미지 최적화

// 이미지 지연 로딩 디렉티브
@Directive({
  selector: '[lazyLoad]'
})
export class LazyLoadDirective implements AfterViewInit {
  @Input('lazyLoad') src: string;

  constructor(private el: ElementRef) {}

  ngAfterViewInit() {
    const obs = new IntersectionObserver(entries => {
      entries.forEach(({ isIntersecting }) => {
        if (isIntersecting) {
          this.el.nativeElement.src = this.src;
          obs.unobserve(this.el.nativeElement);
        }
      });
    });

    obs.observe(this.el.nativeElement);
  }
}

결론 및 향후 발전 방향

이 프로젝트를 통해 우리는 다음과 같은 성과를 달성했습니다:

  1. 안정적인 실시간 통신 구현
  2. 보안이 강화된 사용자 인증
  3. 확장 가능한 데이터베이스 구조
  4. 최적화된 성능

향후 개선 방향:

  • 파일 공유 기능 추가
  • 그룹 채팅 구현
  • 푸시 알림 통합
  • 메시지 검색 기능

관련 태그

#Angular #Supabase #WebDevelopment #RealTimeChat #Authentication #TypeScript #WebApp #Programming #Frontend #Database


이 튜토리얼의 전체 소스 코드는 GitHub에서 확인할 수 있으며, 실제 구현 사례를 통해 학습하실 수 있습니다. 추가 질문이나 제안이 있다면 댓글로 남겨주세요.

참고 문헌

  1. Angular 공식 문서
  2. Supabase 문서
  3. Google OAuth 2.0 가이드
  4. PostgreSQL 문서
728x90

'IT > angular' 카테고리의 다른 글

angular v18이 출시 되었습니다. (Angular v18 is now available)  (0) 2024.09.02