> 웹 프론트엔드 > JS 튜토리얼 > NgSysV.Google&#s Firestore를 사용하여 간단한 Svelte 정보 시스템 만들기

NgSysV.Google&#s Firestore를 사용하여 간단한 Svelte 정보 시스템 만들기

Susan Sarandon
풀어 주다: 2024-11-28 06:23:11
원래의
131명이 탐색했습니다.

이 게시물 시리즈의 색인은 NgateSystems.com에 있습니다. 거기에서 매우 유용한 키워드 검색 기능도 찾을 수 있습니다.

최종 검토일: 2024년 11월

1. 소개

대부분의 웹앱은 순전히 공유 정보를 생성하고 액세스하기 위해 존재합니다. Amazon의 https://www.amazon.co.uk/ 웹사이트를 생각해 보세요. 이 시스템의 기본 목적은 중앙에서 제품 세부 정보를 검색하고, 주문하고, 배송 진행 상황을 모니터링할 수 있도록 하는 것입니다. 이를 실현하려면 Amazon은 다음을 수행해야 합니다.

  • 이 정보를 웹을 통해 액세스할 수 있는 곳에 보관하세요
  • 거의 즉각적인 액세스와 완전한 무결성을 보장하도록 구성하고 관리하세요.

이 게시물은 이러한 목표를 달성하는 데 사용되는 "데이터베이스" 기술에 관한 것입니다.

경고 - Svelte의 데이터베이스 읽기 및 쓰기는 사용자가 SvelteKit의 클라이언트-서버 아키텍처를 사용하도록 유도하기 때문에 이것은 게시물입니다. 이전에는 코드가 웹 브라우저에서 "클라이언트 측"에서만 실행되었습니다. 이제 npm run dev에 의해 시작된 로컬 서버에서도 코드를 실행할 수 있습니다. 이로 인해 결과가 발생합니다...

게시물을 분할하는 방법을 살펴봤지만 작동하지 않습니다. 설상가상으로, 여러분이 사용할 Javascript에는 많은 새로운 기능이 포함되어 있습니다. 그러니까 미안해요. 그냥 삼켜야 할 것 같아요.

하지만 긍정적인 면을 보세요. 이 과정을 마치면 상황이 더 쉬워지기 시작할 것입니다. 천천히 복용하세요. 내가 명확하게 설명하지 않았다고 생각되는 경우에는 chatGPT를 사용하세요. JavaScript 구문에 대한 조언이 필요할 때 봇이 특히 유용하다는 것을 알게 될 것입니다. 안심하다. 재미있을 것 같아요!

2. Google의 Firestore를 사용하도록 프로젝트 구성

웹에 공유 데이터를 저장하는 방법은 무수히 많습니다. 이 게시물 시리즈에서는 초보자에게 적합하기 때문에 Google의 Firestore 시스템을 사용합니다. 최소한의 설정만 필요하며 Svelte 웹앱 구조에 편안하게 맞습니다.

다음 네 가지 초기 단계를 수행해야 합니다.

  1. Google 계정 얻기
  2. 이 계정으로 Firebase 프로젝트 만들기
  3. "웹앱" 등록
  4. Firebase 프로젝트용 Firestore 데이터베이스 초기화

Firebase는 웹에 간단한 프로젝트를 탑재하는 데 사용할 수 있는 다양한 서비스를 가리키는 Google의 포괄적인 용어입니다. 특정 계정의 서비스는 https://console.firebase.google.com/에서 Google의 'Firebase 콘솔'을 통해 관리됩니다. 여기에는 Google Cloud에 파일을 업로드할 수 있는 '스토리지' 서비스와 'Firestore 데이터베이스' 서비스가 모두 포함되어 있습니다. 데이터베이스는 구성 가능한 구조를 가지고 있다는 점에서 파일과 다릅니다. 이를 통해 구성된 데이터 세트의 개별 요소에 액세스하고 업데이트할 수 있습니다.

2.1 Google 계정 얻기

Gmail 주소가 있는 경우 자동으로 Google 계정으로 계산되므로 이미 적용 대상입니다. 그렇지 않은 경우 Google 계정 만들기의 안내에 따라 계정을 만드세요.

2.2 코드용 Firebase 프로젝트 만들기

Google Firebase 콘솔을 실행하고 Google 계정으로 로그인합니다(이 계정으로 Gmail에 로그인했다면 이미 Firebase 콘솔에도 로그인되어 있는 것입니다). 이제 "프로젝트 만들기" 상자를 클릭하여 프로세스를 시작하세요.

Google에서는 프로젝트 이름을 제공하기를 원하며(VSCode에서 사용 중인 프로젝트 이름을 사용하는 것이 좋습니다) 이를 Firebase 세계 내에서 고유한 '프로젝트 식별자'로 만드는 확장 프로그램을 제안할 것입니다. 예를 들어, 이 게시물 시리즈에 사용된 "Svelte-dev" 프로젝트의 my 버전은 Google에서 "Svelte-dev-afbaf"로 알려져 있습니다.

여담이지만, 프로젝트 식별자는 궁극적으로 웹앱의 기본 라이브 URL의 일부를 형성하고 Google에서 초기 '고유성 확장' 제안을 편집할 수 있으므로 이를 변경하고 싶은 유혹을 느낄 수도 있습니다. 의미 있는 일에. 하지만 이 생각은 잊어버리시길 바랍니다. 첫째, 양측 모두에게 적합한 식별자를 선택하기가 어렵습니다. 둘째, 내 경험상 이러한 '기본 URL'은 Google에서 색인을 생성하지 않습니다. 기억에 남는 URL을 얻는 가장 좋은 방법은 최소한의 비용으로 구입하고 라이브 실행 시 기본 URL에 연결되는 "맞춤 URL"입니다.

이제 '계속'을 클릭하면 'Google Analytics' 등록 페이지가 표시됩니다. 라이브 앱의 성능 문제에만 관련되므로 여기서는 무시해도 됩니다. 거부하려면 슬라이더 막대를 사용하고 계속하려면 "프로젝트 만들기" 버튼을 클릭하세요.

이제 Google에서 프로젝트를 등록하면 조명이 약간 어두워집니다. 마지막으로 "계속" 버튼을 하나 더 클릭하면 프로젝트의 Firebase 콘솔 창이 표시됩니다. 다음은 "svelte-dev" 프로젝트에 대한 Firestore 탭의 스크린샷입니다.

NgSysV.Creating a simple Svelte Information System with Google

이 페이지는 조금 복잡하기 때문에 잠시 시간을 내어 익숙해지는 것이 좋습니다. 기본 구조는 오른쪽의 기본 패널에 표시되는 내용을 결정하는 왼쪽의 "도구 메뉴"로 구성됩니다. 문제는 메뉴가 "적응형"이고 현재 위치를 기억하는 "프로젝트 바로 가기" 섹션을 유지한다는 것입니다. 결과적으로 콘솔을 열 때마다 메뉴가 다르게 보이는 것 같습니다! 그러나 이 기능을 이해하고 나면 모든 것이 잘 작동합니다. 전체 도구 세트는 상위 "제품 카테고리" 메뉴 항목의 "빌드", "실행" 및 "분석" 하위 메뉴 내에 숨겨져 있습니다. "빌드" 세트에는 현재 필요한 모든 것이 포함되어 있습니다.

계속 진행하기 전에 다음 사항에 유의하세요.

  • 화면 상단의 정보는 svelte-test 프로젝트가 현재 "Spark" 플랜에 등록되어 있음을 확인시켜 줍니다. 이는 현재 하고 있는 모든 작업이 무료라는 의미입니다. 결국 이 포스트 시리즈에서는 Google에 비용을 지불해야 하고 프로젝트를 "Blaze" 요금제로 업그레이드해야 하는 지점에 도달하게 됩니다. 하지만 걱정하지 마세요. 아직 갈 길이 멀기 때문에 비용을 많이 지불하지 않을 것이며 월예산을 만들어 Google에서 청구하려는 금액을 제한할 수 있습니다. 툴바 상단의 '프로젝트 개요' 버튼을 클릭하면 프로젝트 세부정보가 표시됩니다. 여기에서 확인할 수 있는 세부정보에는 프로젝트 식별자 알림과 프로젝트 삭제 버튼이 포함됩니다. 모든 것이 잘못되면 언제든지 이 방법을 사용하여 문제를 해결하고 다시 시작할 수 있습니다. 비용은 전혀 들지 않습니다
  • 2.3 웹앱 등록

Firebase는 웹 앱의 이름을 알아야 합니다.

"시작하기" 메시지 아래에 아이콘을 표시하고 요청 시 닉네임을 제공하세요. 여기서는 프로젝트 이름을 다시 사용하는 것이 좋습니다(예: "svelte-dev").
  • '이 앱에 Firebase 호스팅을 설정하세요'라는 제안은 무시하세요. 최종적으로 배포가 완료되면 모든 호스팅 요구 사항이 App Engine에서 처리되기 때문입니다.
  • 마지막으로 "등록"과 "콘솔로 계속"을 클릭하여 초기 콘솔 화면으로 돌아갑니다.
  • 2.4 - Firestore 데이터베이스 초기화

도구 메뉴의 '빌드' 스택에서 'Firestore 데이터베이스'를 선택하면 아래와 같은 Firebase 콘솔 보기가 표시됩니다.

NgSysV.Creating a simple Svelte Information System with Google"데이터베이스 생성" 버튼을 클릭하면 콘솔에서 다음을 수행합니다.

    데이터베이스 "이름 및 위치"를 설정하세요. "이름"은 데이터베이스의 식별자이며 프로젝트에서 여러 다른 데이터베이스를 생성하려는 경우에만 관련됩니다. 지금은 Google이 '기본' 설정을 사용할 수 있도록 이 항목을 비워 두세요. "위치"는 데이터베이스가 물리적으로 위치할 위치를 지정합니다. 여기에서 사용할 수 있는 옵션의 풀다운 목록은 아마도 Google Cloud 서비스 규모를 가장 먼저 확인할 수 있는 것입니다. 서버 팜은 전 세계에서 바로 사용할 수 있습니다. 아마도 귀하의 위치에 가까운 서버를 선택하고 싶을 것입니다. 예를 들어 저는 영국에 거주하고 있기 때문에 일반적으로 "europe-west2 : Heathrow"를 사용합니다. Google Cloud 콘솔의 다른 페이지에서 성능 및 가용성 특성을 지정할 수 있지만 지금은 이러한 특성을 볼 필요가 없습니다.
  1. "규칙"으로 데이터베이스를 보호하세요. 여기 화면에서는 초기 "생산" 설정과 "테스트" "규칙" 설정 중에서 선택할 수 있습니다. 물론 이것은 "규칙"이 무엇인지 아는 경우에만 의미가 있습니다. 그리고 지금은 제가 이에 대해 설명할 적절한 시기가 아닙니다. 더 잘 아시는 분이 아니라면 여기서 '테스트 모드' 옵션을 확인해 주시길 바랍니다. 나중에 "승인"에 대해 이야기할 때 이 부분에 대해 다시 설명하겠습니다(아, 할 얘기가 너무 많네요!).

이 두 단계를 완료하면 Firebase 콘솔에 Cloud Firestore 페이지가 열립니다. 이제 어쩌죠?

3. Firestore 데이터베이스 작업

이 섹션은 다음 질문에 답하는 것을 목표로 합니다.

  1. 데이터베이스란 무엇인가요?
  2. Firestore 데이터베이스는 어떤 모습인가요?
  3. Firestore 콘솔에서 데이터베이스를 어떻게 초기화할 수 있나요?
  4. Javascript로 Firestore 데이터베이스에 어떻게 액세스할 수 있나요?
  5. Firestore 데이터베이스에서 데이터를 로드하기 위해 Svelte page.svelte 파일을 어떻게 얻을 수 있나요?
  6. Firestore 데이터베이스에 데이터를 추가하기 위해 Svelte page.svelte 파일을 어떻게 얻을 수 있나요?

3.1 데이터베이스란 무엇인가?

당장 목적을 위해 데이터베이스는 명명된 데이터 "필드"에 대한 값 행을 포함하는 테이블 집합입니다. 예를 들어 고객 주문" 데이터베이스에는

이 포함될 수 있습니다.
  • "고객 ID" 및 "고객 주소 세부 정보" 필드 값으로 가득 찬 "고객" 테이블
  • "제품 번호" 및 "제품 세부 정보" 필드로 구성된 "제품" 테이블
  • "고객 ID"에 의한 "제품 번호" 주문 세부정보가 포함된 "고객 주문" 테이블

중요한 것은 이러한 배열이 데이터 콘텐츠의 이름 지정 및 형식 지정에 대한 일관된 표준에 따라 구조화

된다는 것입니다.

3.2 Firestore 데이터베이스는 어떤 모습인가요?

Firestore에서는 테이블을 '컬렉션'이라고 하고 그 안의 행을 '문서'라고 합니다. 컬렉션 내에 저장된 문서가 모두 동일한 필드를 가질 필요는 없지만 필드 이름과 콘텐츠는 컬렉션 전체에서 일관된 패턴을 따라야 합니다.

Firestore 문서의 중요한 특징은 고유 식별자 또는 '키'가 있어야 한다는 것입니다. 때로는 "자연스러운" 고유 키를 제공하는 데 사용할 수 있는 "이메일 주소"와 같은 필드가 각 문서 내에 있을 수 있습니다. 그렇지 않은 경우 Firestore에서 자동으로 인공 키를 생성하도록 요청할 수 있습니다.

데이터베이스 설계는 아마도 시스템 개발에서 가장 어려운 부분일 것입니다. 다시 한 번 말씀드리지만, 관련된 문제는 실제 경험을 겪어본 후에야 명확해질 수 있기 때문에 이 점을 피하겠습니다. 하지만 시간이 나면 Cloud Firestore 데이터 모델 페이지를 확인해 보는 것이 도움이 될 것입니다.

3.3 Firestore 콘솔에서 데이터베이스를 어떻게 초기화할 수 있나요?

이 게시물에서는 기본 Firestore 데이터베이스에 단일 제품 컬렉션을 생성하는 방법을 보여드릴 계획입니다. 여기에는 Firestore에서 자동으로 생성된 키가 있는 제품 번호 필드가 포함된 간단한 문서가 포함됩니다.

Firebase 콘솔의 Firestore 페이지에서 '컬렉션 시작' 버튼을 클릭하고 나타나는 팝업의 '컬렉션 ID' 필드에 'products' 이름을 입력하세요.

NgSysV.Creating a simple Svelte Information System with Google

이제 데이터 입력 페이지를 사용하여 숫자 값이 "1"인 "productNumber" 필드와 텍스트 값이 "Product 1"인 "productDetails" 필드가 포함된 테스트 제품 문서를 만듭니다.

  • 필드 이름을 설정하려면 "필드" 입력 상자에 "productNumber"를 입력하고, "유형" 상자를 "숫자"로 설정한 다음 "값" 상자에 "1"(따옴표 제외)을 입력합니다.
  • "필드 추가"를 클릭하고 "필드" 입력 상자에 "productDetails"를 입력하여 필드 이름을 설정합니다. "유형" 상자를 기본 "문자열" 설정으로 두고 "제품 1"을 입력합니다(따옴표 제외). )를 '값' 상자에 입력하세요.

이제 먼저 "자동 ID" 버튼을 클릭한 다음 "저장"하여 문서를 로그오프합니다. 이제 콘솔은 다음과 같습니다.

NgSysV.Creating a simple Svelte Information System with Google

더 많은 문서를 추가하려면 이 시점에서 "문서 추가"를 클릭해야 하지만 이 경우에는 그럴 필요가 없습니다. 웹앱이 문서를 읽을 수 있음을 보여주기 위해 단일 문서만 있으면 됩니다.

이제 끝났습니다. 콘솔의 '패널 보기'를 사용하면 방금 만든 문서를 편집하거나 삭제할 수 있습니다. 완전히 엉망이 된 경우 전체 컬렉션을 삭제하고 다시 시작할 수도 있습니다.

3.4 자바스크립트로 Firestore 데이터베이스에 어떻게 액세스하나요?

여기서부터 정말 흥미로워지기 시작합니다!

Google은 Firestore 문서를 읽고 쓸 수 있는 자바스크립트 함수 라이브러리를 제공합니다. 이러한 라이브러리를 "API"(응용 프로그램 인터페이스)라고 합니다. svelte-dev 제품 컬렉션의 모든 문서를 읽는 데 firebase/firestore 라이브러리를 사용하는 방법을 보여주는 다음 코드를 살펴보세요.

import { collection, query, getDocs, orderBy } from "firebase/firestore";
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";

const firebaseConfig = {
    apiKey: "AIzaSyCE933 ... klfhFdwQg1IF1pWaR1iE",
    authDomain: "svelte-dev-afbaf.firebaseapp.com",
    projectId: "svelte-dev-afbaf",
    storageBucket: "svelte-devt-afbaf.appspot.com",
    messagingSenderId: "1027 ... 85697",
    appId: "1:1027546585697:web:27002bf ..... b0f088e820",
};

const firebaseApp = initializeApp(firebaseConfig);
const db = getFirestore(firebaseApp);

const productsCollRef = collection(db, "products");
const productsQuery = query(productsCollRef, orderBy("productNumber", "asc"));
const productsSnapshot = await getDocs(productsQuery);

let currentProducts = [];

productsSnapshot.forEach((product) => {
    currentProducts.push({productNumber: product.data().productNumber});
});

return { products: currentProducts } // accessed in +page.svelte as data.products
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

const productsCollRef = collection(db, "products");로 시작하는 섹션에 집중하세요. 이는 Firestore API 호출을 사용하여 제품 컬렉션 내 모든 문서의 정렬된 복사본을 State currentProducts 변수에 로드합니다.

첫째, Firestore 클라이언트 API 라이브러리에서 가져온 수집 및 쿼리 함수는 Firebase를 제품 컬렉션으로 가리키고 여기에서 실행할 쿼리를 정의하는 데 사용됩니다. 그런 다음 getDocs API 호출을 통해 쿼리가 시작됩니다.

이 Firestore API 호출 시퀀스의 메커니즘을 설명하지는 않겠습니다. 이것을 "보일러 플레이트 코드"(코드)로 취급하십시오. 즉, 한 번 작성하고 나중에는 간단히 복사하는 종류의 것입니다. Firestore의 전체 "읽기", "업데이트" 및 "삭제" 작업을 처리하려면 전체 템플릿 라이브러리가 필요하므로 10.1 이후 - Firestore CRUD 명령 템플릿을 살펴보는 것이 유용할 수 있습니다. API에 대한 Google의 자체 설명을 확인하려면 Post 10.1 끝부분에서 이에 대한 링크를 찾을 수 있습니다.

여기서 "CRUD"는 "만들기", "읽기", "업데이트", "삭제"의 약어입니다.

getDocs 결과는 일반적으로 "스냅샷"이라고 불리는 문서 배열로 반환됩니다. 그러나 getDocs 함수 호출 앞에는 wait 키워드가 옵니다.

기본적으로 Javascript에서는 완료하는 데 예측할 수 없는 시간이 걸릴 수 있는 외부 데이터 소스를 참조하는 명령이 비동기적으로 처리되기 때문에 여기서 wait 키워드가 필요합니다. 기본적으로 "await" 키워드를 사용하면(엄청난 단순화임에도 불구하고) 이 배열을 재정의할 수 있습니다. 시간이 더 있으면 Javascript fetch() API 및 "await" 키워드에 대한 간단한 가이드

를 살펴보는 것이 유용할 수 있습니다.

하지만 지금은 위의 코드 조각으로 돌아가서 const firebaseConfig 문으로 시작하는 섹션을 살펴보세요.

firebaseConfig 선언은 웹 앱을 특정 Firebase 프로젝트에 연결하는 데 필요한 구성 세부정보가 포함된 객체를 초기화합니다. 여기에는 Firebase가 앱을 찾고 인증하는 데 사용하는 다양한 키와 식별자가 포함되어 있습니다. Firebase 콘솔의 '프로젝트 개요/프로젝트 설정'에서 귀하의 특정 웹앱에 대한 설정을 찾을 수 있습니다. 아래 코드 샘플의 firebaseConfig 설정은 my 프로젝트에 고유하고 가볍게 전달되어서는 안 되기 때문에 '난독화'되었습니다(자세한 내용은 곧 설명). 아래 샘플 코드를 시도할 때는 자신의 프로젝트에서 firebaseConfig 설정을 복사해야 합니다.

firebaseConfig가 초기화되면 웹 앱은 쿼리의 const productsCollRef = collection(db, "products");에 필요한 db 변수를 초기화할 수 있습니다. 진술:

import { collection, query, getDocs, orderBy } from "firebase/firestore";
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";

const firebaseConfig = {
    apiKey: "AIzaSyCE933 ... klfhFdwQg1IF1pWaR1iE",
    authDomain: "svelte-dev-afbaf.firebaseapp.com",
    projectId: "svelte-dev-afbaf",
    storageBucket: "svelte-devt-afbaf.appspot.com",
    messagingSenderId: "1027 ... 85697",
    appId: "1:1027546585697:web:27002bf ..... b0f088e820",
};

const firebaseApp = initializeApp(firebaseConfig);
const db = getFirestore(firebaseApp);

const productsCollRef = collection(db, "products");
const productsQuery = query(productsCollRef, orderBy("productNumber", "asc"));
const productsSnapshot = await getDocs(productsQuery);

let currentProducts = [];

productsSnapshot.forEach((product) => {
    currentProducts.push({productNumber: product.data().productNumber});
});

return { products: currentProducts } // accessed in +page.svelte as data.products
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

마지막으로 이러한 API 함수가 어디서 왔는지 궁금하실 것입니다. 대답은 코드 블록 상단에 있는 세 가지 문을 통해 프로젝트의 위치에서 가져오기된다는 것입니다.

const firebaseApp = initializeApp(firebaseConfig);
const db = getFirestore(firebaseApp);
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

여기서 "모듈형 라이브러리"는 코드에 대한 기능을 제공하기 위해 액세스됩니다. 컬렉션과 같은 명명된 함수는 나중에 코드에서 필요한 참조를 충족하기 위해 상위 모듈에서 추출됩니다.

그러나 이는 "그리고 애초에 모듈형 라이브러리가 내 프로젝트에 어떻게 들어가는가?"라는 질문으로 이어집니다. 물론 대답은 그것들을 거기에 넣어야 한다는 것이고, 이를 위해 사용하는 도구는 충실한 오래된 npm입니다.

VSCode svelte-test 터미널 세션으로 돌아가서(필요한 경우 Ctrl-C 키를 몇 번 눌러 개발 서버 종료) 다음 명령을 실행하세요.'

import { collection, query, getDocs, orderBy } from "firebase/firestore";
import { initializeApp } from "firebase/app";
import { getFirestore } from "firebase/firestore";

const firebaseConfig = {
    apiKey: "AIzaSyCE933 ... klfhFdwQg1IF1pWaR1iE",
    authDomain: "svelte-dev-afbaf.firebaseapp.com",
    projectId: "svelte-dev-afbaf",
    storageBucket: "svelte-devt-afbaf.appspot.com",
    messagingSenderId: "1027 ... 85697",
    appId: "1:1027546585697:web:27002bf ..... b0f088e820",
};

const firebaseApp = initializeApp(firebaseConfig);
const db = getFirestore(firebaseApp);

const productsCollRef = collection(db, "products");
const productsQuery = query(productsCollRef, orderBy("productNumber", "asc"));
const productsSnapshot = await getDocs(productsQuery);

let currentProducts = [];

productsSnapshot.forEach((product) => {
    currentProducts.push({productNumber: product.data().productNumber});
});

return { products: currentProducts } // accessed in +page.svelte as data.products
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사
로그인 후 복사

1~2분 후에(설치에는 상당한 규모의 다운로드가 포함됨) Firestore 데이터베이스 컬렉션을 다운로드하는 코드를 실행할 준비가 됩니다. 그러나 Svelte 웹앱에 이를 포함하는 방법을 여전히 모릅니다. 그럼 다음 질문으로 넘어가겠습니다...

3.5 Firestore 데이터베이스에서 데이터를 로드하기 위해 Svelte page.svelte 파일을 얻으려면 어떻게 해야 합니까?

오랜 시간이 걸렸지만 조금만 기다려주세요. 이제 거의 다 끝났습니다.

현재

저자별 최신 기사
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿