Github Actions - React ํ”„๋กœ์ ํŠธ๋ฅผ Firebase์— Deploy ํ•˜๊ธฐ

๋ฐ˜์‘ํ˜•

Actions ๋งˆ์ง€๋ง‰ ํฌ์ŠคํŠธ๋กœ React ํ”„๋กœ์ ํŠธ๋ฅผ Firebase์— Deploy ํ•˜๋Š” ๊ธ€์„ ์จ๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์ง€๋‚œ ํฌ์ŠคํŠธ์—์„œ YAML ํŒŒ์ผ์„ ๊ฐ€์ง€๊ณ  ๊ฐ„๋‹จํ•œ Workflow์™€ Matrix์™€ Secrets ๋“ฑ์˜ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊นŒ์ง€๋ฅผ ๋‹ค๋ค„๋ดค์Šต๋‹ˆ๋‹ค.

 

์ด๋ฒˆ ๊ธ€์—์„œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ Google Firebase์— ๋Œ€ํ•œ ๊ธฐ๋ณธ์ ์ธ ์ง€์‹์ด ์žˆ๋Š” ์ƒํƒœ์—์„œ ์ง„ํ–‰์„ ํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. Firebase์˜ ์ฒ˜์Œ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๊ฒŒ ๋˜๋ฉด ๊ธ€์ด ๊ธธ์–ด์ง€๊ธฐ ๋•Œ๋ฌธ์—, ๊ฐ„๋‹จํ•˜๊ฒŒ ์š”์ ๋งŒ ์งš๊ณ  ๋„˜์–ด๊ฐ€๊ธฐ ์œ„ํ•ด์„œ Firebase์˜ ๊ธฐ๋ณธ ํ™˜๊ฒฝ์ด ์ด๋ฏธ ๊ตฌ์ถ•๋œ ์ƒํƒœ์—์„œ ์ง„ํ–‰ํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

 

Generate and Register Firebase Token

๋ฐฐํฌ ๋„๊ตฌ๋กœ Google Firebase์—์„œ ๊ณต์‹์ ์œผ๋กœ ์ œ๊ณตํ•˜๋Š” firebase-tools๋ฅผ ํ•œ ๋ฒˆ ์‚ฌ์šฉํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ์ด ํˆด์€ Node.js ๊ธฐ๋ฐ˜์œผ๋กœ ๋˜์–ด ์žˆ์–ด์„œ Node Package Manager (์ดํ•˜ NPM)์„ ์ด์šฉํ•ด ์„ค์น˜ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

$ npm install -g firebase-tools

ํ˜น์€ Yarn์„ ์‚ฌ์šฉํ•˜์‹ค ๋ถ„์ด์‹œ๋ผ๋ฉด, ์•„๋ž˜์˜ ๋ช…๋ น์–ด๋„ ๊ดœ์ฐฎ๊ฒ ์ฃ ?

$ yarn global add firebase-tools

์ด๋ ‡๊ฒŒ firebase-tools ์„ค์น˜๊ฐ€ ๋๋‚ฌ์œผ๋ฉด firebase ๋ฐฐํฌ ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•˜์‹ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ž ์—ฌ๊ธฐ์„œ ์šฐ๋ฆฌ๊ฐ€ ํ•„์š”ํ•œ ๊ฒƒ์€ ๋ฐ”๋กœ Firebase ๋ฐฐํฌ์‹œ ์‚ฌ์šฉํ•  ์ธ์ฆ Token์ž…๋‹ˆ๋‹ค. ์ด Token์€ ์—ฌ๋Ÿฌ๋ถ„์˜ ๋ฐฐํฌ ํ™˜๊ฒฝ์ด๋ผ๋Š” ํ‘œ์‹์˜ ์ธ์ฆํ‚ค์ธ๋ฐ์š”. ์ด ํ‚ค๊ฐ€ ์—†์œผ๋ฉด Firebase๊ฐ€ ์–ด๋””๋กœ ๋ฐฐํฌํ• ์ง€๋ฅผ ์•Œ ์ˆ˜๊ฐ€ ์—†๊ฒ ์ฃ ? ๋”ฐ๋ผ์„œ ์ด๋ฅผ ์ƒ์„ฑํ•ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.

$ firebase login:ci

firebase login ๋ช…๋ น์–ด์— ci์˜ argument๋ฅผ ๋ถ™์ด๊ฒŒ ๋˜๋ฉด, ์›น ํŽ˜์ด์ง€๊ฐ€ ๋‚˜์™€ ์ž์‹ ์˜ Google ๊ณ„์ •์œผ๋กœ ๋กœ๊ทธ์ธํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค. ๋กœ๊ทธ์ธ์ด ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋˜๋ฉด, ์•„๋ž˜์™€ ๊ฐ™์ด ํ„ฐ๋ฏธ๋„์— ์ž์‹ ์˜ Token์ด ํ‘œ์‹œ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

 

์ž์‹ ์˜ Token์ด ์ƒ์„ฑ์ด ๋˜์—ˆ์œผ๋ฉด ์ด์ œ ์ด Token์„ Repository์˜ Secrets์— ์ถ”๊ฐ€ํ•ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

 

Repository์˜ Settings ํ•ญ๋ชฉ์— ๊ฐ€์‹œ๋ฉด, Secrets ํ•ญ๋ชฉ์ด ์ƒ๊ฒจ์ ธ ์žˆ๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Add a new secret ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์—ฌ FIREBASE_TOKEN์ด๋ผ๋Š” ์ด๋ฆ„์œผ๋กœ Secrets๋ฅผ ์ƒ์„ฑํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค..

 

 

 

Create Workflow

์ž ์ด์ œ ์›Œํฌํ”Œ๋กœ์šฐ๋ฅผ ๋งŒ๋“ค์–ด๋ณด๋„๋ก ํ•˜์ฃ . ์—ฌ๊ธฐ์„œ ์ €ํฌ๊ฐ€ ๋งŒ๋“ค ์›Œํฌํ”Œ๋กœ์šฐ๋Š” 2๊ฐœ์ž…๋‹ˆ๋‹ค. React ํ”„๋กœ์ ํŠธ๋Š” ์›น ํ”„๋กœ์ ํŠธ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋นŒ๋“œํ•˜๊ณ  ํ…Œ์ŠคํŠธํ•˜๋Š” ๊ณผ์ •์ด 1๊ฐœ ์žˆ์–ด์•ผ ํ•˜๊ณ , ๊ทธ๋ฆฌ๊ณ  ๋งˆ์ง€๋ง‰์œผ๋กœ ๋นŒ๋“œ์™€ ํ…Œ์ŠคํŠธ๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ๋๋‚ฌ๋‹ค๋ฉด ์‹ค์ œ ํด๋ผ์šฐ๋“œ๋‚˜ On-Premise ํ™˜๊ฒฝ์— ๋ฐฐํฌํ•˜๋Š” ๊ฒƒ๊นŒ์ง€๊ฐ€ ์ตœ์ข… ๊ฐœ๋ฐœ ๋ฐ ํ…Œ์ŠคํŠธ ํ”„๋กœ์„ธ์Šค๊ฐ€ ๋˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

๊ทธ๋ ‡๋‹ค๋ฉด ์–ด๋–ป๊ฒŒ ๊ตฌ์„ฑํ•˜๋ฉด ๋ ๊นŒ์š”? master ๋ธŒ๋žœ์น˜๋ฅผ Productionํ•˜๋Š” ๋ธŒ๋žœ์น˜๋ผ๊ณ  ๊ฐ€์ •ํ•˜๊ณ  ๊ทธ ์™ธ์˜ ๋ธŒ๋žœ์น˜๋ฅผ Develop ํ•˜๋Š” ๋ธŒ๋žœ์น˜๋ผ๊ณ  ๊ฐ„๋‹จํ•˜๊ฒŒ ๊ตฌ์„ฑํ•œ๋‹ค๋ฉด, master์— push ๋˜๋Š” ๋‹จ๊ณ„์—์„œ Deploy๋ฅผ ์ˆ˜ํ–‰ํ•˜๋ฉด ๋˜๊ณ , ๊ทธ ์™ธ์˜ ๋ธŒ๋žœ์น˜์—์„œ๋Š” Pushํ•˜๋Š” ์ด๋ฒคํŠธ๋ฅผ ํ—ˆ์šฉํ•˜ ๋˜, master ๋ธŒ๋žœ์น˜๋กœ PR์„ ์š”๊ตฌํ•œ๋‹ค๋ฉด, ๊ทธ ๋•Œ ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ•˜๋„๋ก ๋งŒ๋“ค๋ฉด ๋˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

Firebase Deployment Flow

๋‹ค์ด์–ด๊ทธ๋žจ์œผ๋กœ ํ‘œ์‹œํ•˜๋ฉด ๋Œ€์ถฉ ์ด๋ ‡๊ฒŒ ๋  ๊ฒƒ ๊ฐ™๋„ค์š”. ๋‹ค๋ฅธ ๋ธŒ๋žœ์น˜๋ฅผ ๋งŒ๋“ค๊ณ  PR์„ ์˜ฌ๋ฆฌ๊ฒŒ ๋˜๋ฉด, Github์—์„œ Actions์—๊ฒŒ ๋นŒ๋“œ ์ด๋ฒคํŠธ๋ฅผ ๋ณด๋‚ด๊ฒŒ ๋˜๊ตฌ์š”. ๊ทธ ์ƒํƒœ๋ฅผ Github์—์„œ ๋ฐ›๊ณ  Merge๊ฐ€ ์ด๋ฃจ์–ด์ง€๋ฉด Firebase๋กœ Deploy ํ•˜๋Š” ํ˜•ํƒœ์ž…๋‹ˆ๋‹ค.

 

๊ทธ๋Ÿผ ์ฒซ ๋ฒˆ์งธ Workflow๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ฃ .

name: React Build

on: [ pull_request ]

jobs:
  build:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [10.x]

    steps:
      - name: Set up Node.js ${{ matrix.node-version }} 
        uses: actions/setup-node@v1
        with:
          node-version: ${{ matrix.node-version }}
      - uses: actions/checkout@v1
      - name: npm install, build
        run: |
          npm install
          npm run build --if-present

1๋ฒˆ์งธ Workflow๋Š” PR ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์ด๋ค„์ง€๋Š” ์›Œํฌํ”Œ๋กœ์šฐ์ž…๋‹ˆ๋‹ค. on ํด๋ž˜์Šค์— pull_request ์ด๋ฒคํŠธ๋งŒ์„ ์ฃผ์–ด์คฌ๊ธฐ ๋•Œ๋ฌธ์— ์–ด๋–ค ๋ธŒ๋žœ์น˜์—์„œ๋“ ์ง€ PR์ด ๋ฐœ์ƒํ•˜๋ฉด ์ด ์›Œํฌํ”Œ๋กœ์šฐ๊ฐ€ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. 

name: React Deploy

on:
  push: 
    branches:
      - master

jobs:
  build_and_deploy:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [10.x]

    steps:
      - name: Set up Node.js ${{ matrix.node-version }} 
        uses: actions/setup-node@v1
        with:
          node-version: ${{ matrix.node-version }}
      - uses: actions/checkout@v1
      - name: npm install, build
        run: |
          npm install
          npm run build --if-present 
      - name: Deploy Firebase
      	env:
          FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
        run: |
          npm install firebase-tools
          firebase deploy --token $FIREBASE_TOKEN --non-interactive

2๋ฒˆ์งธ Workflow๋Š” master์— Push ํ–ˆ์„ ๋•Œ ์ด๋ค„์ง€๋Š” ์›Œํฌํ”Œ๋กœ์šฐ์ง€์š”. ์‚ฌ์‹ค ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ทธ๋ƒฅ master์— Pushํ–ˆ์„ ๋•Œ๋„ ์ด ๋ฐฐํฌ๊ฐ€ ์ง„ํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์— ์‹ค์ œ ์‚ฌ์šฉํ•  ๋•Œ๋Š” master branch์˜ ๊ถŒํ•œ์„ ๊ฐ•ํ™”ํ•˜๋Š” ๊ฒƒ์ด ์ข‹๊ฒ ๋„ค์š”.

 

๋งˆ์ง€๋ง‰์œผ๋กœ firebase deploy ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์•„๊นŒ ์ €์žฅํ•œ FIREBASE_TOKEN์„ ๋ถˆ๋Ÿฌ์ฃผ์‹œ๋ฉด ๋˜๊ฒ ์Šต๋‹ˆ๋‹ค.

 

 

Results

๊ทธ๋Ÿผ ์ด์ œ PR์„ ํ•œ ๋ฒˆ ์˜ฌ๋ ค๋ณผ๊นŒ์š”? 

PR์„ ์˜ฌ๋ฆฌ๋ฉด ์ด๋ ‡๊ฒŒ ์ฒซ ๋ฒˆ์งธ Workflow์˜ ์ด๋ฆ„์ด ๋‚˜ํƒ€๋‚˜๊ณ , Actions์—์„œ ๋นŒ๋“œ ํ™˜๊ฒฝ์„ ์ƒ์„ฑํ•œ ๋‹ค์Œ, ๋นŒ๋“œ๊ฐ€ ์ด๋ฃจ์–ด์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. 

๋นŒ๋“œ๊ฐ€ ์„ฑ๊ณต์ ์œผ๋กœ ์™„๋ฃŒ๋˜๋ฉด ์ดˆ๋ก์ƒ‰ ์ฒดํฌ ํ‘œ์‹œ๋ฅผ, ์‹คํŒจํ•˜๋ฉด ๋นจ๊ฐ„์ƒ‰์˜ X ํ‘œ์‹œ๋ฅผ ๊ฑด๋‚ด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰์œผ๋กœ Merge๋ฅผ ์ง„ํ–‰ํ•˜๋ฉด master ๋ธŒ๋žœ์น˜์— Push ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด์„œ ์•„๊นŒ ๋งŒ๋“ค์—ˆ๋” 2๋ฒˆ์งธ ์›Œํฌํ”Œ๋กœ์šฐ๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. 2๋ฒˆ์จฐ ์›Œํฌํ”Œ๋กœ์šฐ์—๋Š” Firebase๋กœ Deploy๊นŒ์ง€๊ฐ€ ๋‹ด๊ฒจ์ ธ ์žˆ์—ˆ์ฃ ?

Master ๋ธŒ๋žœ์น˜์— Merge๋ฅผ ์‹œ๋„ํ•˜๋ฉด ์ƒˆ๋กœ์šด ์ปค๋ฐ‹์ด ๋งŒ๋“ค์–ด์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. Actions์— ๊ฐ€๋ณด์‹œ๊ฒŒ ๋˜๋ฉด, PR #2๋ฒˆ์— ๋Œ€ํ•œ 2๋ฒˆ์งธ ์›Œํฌํ”Œ๋กœ์šฐ๊ฐ€ ์‹คํ–‰๋˜๊ณ  ์žˆ์Œ์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. 

 

 

๋งˆ์น˜๋ฉฐ...

์—ฌ๊ธฐ๊นŒ์ง€ Github Actions์— ๋Œ€ํ•œ ๊ธ€์— ์ ์–ด๋ดค์Šต๋‹ˆ๋‹ค. Github Actions๋Š” ์•„์ง ๊ณต์‹์ ์œผ๋กœ Github์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ์€ ์•„๋‹™๋‹ˆ๋‹ค. ํ˜„์žฌ๋Š” Beta๋กœ ์ œ๊ณตํ•˜๊ณ  ์žˆ๊ณ , ์‹ ์ฒญ์ž ์ค‘ ๋ฉ”์ผ์„ ๋ฐ›์€ ์‚ฌ๋žŒ์— ๋Œ€ํ•ด์„œ๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์ž…๋‹ˆ๋‹ค.

 

์ฒซ ๊ธ€์—์„œ๋„ ๋ง์”€๋“œ๋ ธ์—ˆ์ง€๋งŒ Github Actions๊ฐ€ Travis CI๋ฅผ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ๊ธฐ์—๋Š” Travis CI ๋ณธ์—ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์‹ค์ƒ ๋Œ€์ฒด๋ผ๊ณ  ๋ณด๊ธฐ์—๋Š” ์–ด๋ ต๊ณ  Collaboration๋œ ํ˜•ํƒœ๋ผ๊ณ  ๋ณด๋ฉด ์‰ฌ์šธ ๋“ฏํ•ฉ๋‹ˆ๋‹ค. ์ถ”๊ฐ€๋กœ ์—ฌ๊ธฐ์— Linux / OS X ํ™˜๊ฒฝ๋งŒ ์ง€์›ํ–ˆ๋˜ Travis CI์— Windows ํ™˜๊ฒฝ๊นŒ์ง€ ์ถ”๊ฐ€๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ฝœ๋ผ๋ณด๊ฐ€ ์˜คํžˆ๋ ค ๋” ์–ด์šธ๋ฆฌ๋Š” ํ˜•ํƒœ๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

 

๋ณธ๋ž˜ Github Actions๋Š” HCL๋กœ ์ž‘์„ฑํ•˜์—ฌ Visual Editor๊ฐ€ ์ œ๊ณต๋œ ๋ฐ” ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ HCL์€ 2019๋…„ 9์›” 30์ผ๋ถ€๋กœ ์™„์ „ํžˆ ์ œ๊ฑฐ๋˜์–ด ์•ž์œผ๋กœ๋Š” ๊ณ„์† YAML๋งŒ ์‚ฌ์šฉํ•  ๊ฒƒ์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

Github Actions

 

GitHub Actions

Get started with one of our guides, or jump straight into the API documentation.

developer.github.com

๊ฐ„๋‹จํ•œ ํ›„๊ธฐ๋ฅผ ์ž‘์„ฑํ•ด๋ณด์ž๋ฉด, Github Actions์—์„œ๋Š” Github์˜ ๊ฐ์ข… ์ด๋ฒคํŠธ(Push, Pull-request ๋“ฑ)์„ ์ง์ ‘ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋” ๊ฐ•ํ™”๋œ Ci/CD ํ™˜๊ฒฝ์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด ์žฅ์ ์ž…๋‹ˆ๋‹ค. ๋˜, ๊ฐ ๋นŒ๋“œ ์ƒํƒœ๋ฅผ Success, Failure ๋“ฑ์˜ ํ•จ์ˆ˜๋กœ ์ง€์›ํ•˜์—ฌ ์‹คํŒจํ–ˆ์„ ๊ฒฝ์šฐ์—๋Š” ์‚ฌ์šฉ์ž ์ •์˜์˜ Actions๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์–ด ๋ณด๋‹ค ๋‹ค์–‘ํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ด์šฉํ•ด์„œ ๋งŒ๋“ค์—ˆ๋˜ PR ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์„ Actions ํ•œ ๊ฐœ๋กœ๋„ ์ถฉ๋ถ„ํžˆ ๊ฐ€๋Šฅํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด ๋‹๋ณด์˜€์Šต๋‹ˆ๋‹ค.

 

Docker์™€์˜ ์ง์ ‘ ์—ฐ๋™์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋Š” ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํฌ์ŠคํŠธ์—์„œ๋Š” ๋‹ค๋ฃจ์ง€ ์•Š์•˜์ง€๋งŒ ์‹ค์ œ๋กœ Github Actions๋Š” Docker์™€ ์•„์ฃผ ์นœ๊ทผํ•˜๊ฒŒ ์ ์šฉ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. Docker Hub์™€ ๊ฐ™์€ ์™ธ๋ถ€ Docker ๋ ˆํฌ์ง€ํ„ฐ๋ฆฌ์—์„œ ์‰ฝ๊ฒŒ ์ด๋ฏธ์ง€๋ฅผ ๋‹ค์šด๋กœ๋“œ ๋ฐ›์„ ์ˆ˜ ์žˆ๊ณ , ๋˜ ํŒŒ๋ผ๋ฏธํ„ฐ๋„ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. 

๋ฐ˜์‘ํ˜•
TAGS.

Tistory Comments