IT/angular

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

higold 2024. 11. 1. 07:28

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 문서
반응형

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

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