본문 바로가기
부트캠프 개발일지 2023-2024/TypeScript 타입스크립트

[11주차] 타입스크립트: 리액트환경에서 사용하기

by whereanna00 2023. 12. 13.

타입스크립트: 리액트환경에서 사용하기

 

1. 배열

더보기

 

let 변수명:string[] = ['a', 'b'];

let fruits:string[] = ['apple', 'banana'];
let age:number[]= [ 1, 2, 3 ];

 

 

2. 튜플 (tuple)

더보기

서로 다른 타입의 원소를 순서에 맞게 가질 수 있는 특수한 형태의 배열.

배열은 number[], string[] 처럼 같은 타입의 원소만 가질 수 있었지만, tuple로 인해 서로 다른 타입의 원소를 가진 배열을 만들수있게 됨.

정의된 데이터 타입의 개수와 순서에 맞추어 저장을 하는 것이 필수

const person: [string, number, boolean] = ['Spartan', 25, false];
const person2: [string, number, boolean] = [25, 'Spartan', false]; // 오류!

 

여기서 잠깐! person2에서 정의된 데이터 타입의 개수보다 더 저장할 순 있다. 하지만 억지로 데이터를 더 넣으면 튜플 구조가 내부적으로 변경되어 좋은 선택은 아니다.

 

 

3. enum

= 열거형 데이터 타입

더보기

다양한 상수를 보다 더 이해하기 쉬운 문자열 이름으로 접근하고 사용할 수 있게 하는 타입.

enum 안에 있는 각 요소는 값이 설정되어 있지 않으면 기본적으로 숫자 0으로 시작.

enum 안에 있는 요소에는 number 혹은 string타입의 값만을 할당할 수 있다.

 

하지만

자바스크립트는 열거형(Enum)을 직접 지원하지 않는다. 대신, 객체를 활용하여 비슷한 효과를 낼 수 있다.

먼저, 각각의 상수를 객체의 속성으로 정의한다. 이때, 속성값은 해당 상수를 나타내는 값을 할당한다.

const Days = {
  Sunday: 'Sunday',
  Monday: 'Monday',
  Tuesday: 'Tuesday',
  Wednesday: 'Wednesday',
  Thursday: 'Thursday',
  Friday: 'Friday',
  Saturday: 'Saturday'
};

 

이제 정의한 Enum 객체를 사용하여 변수에 상수값을 할당하고, 필요한 곳에서 이를 활용할 수 있다.

// 사용 예시
let today = Days.Wednesday;
console.log('Today is: ' + today);

 

 

4. readonly

더보기

TypeScript에서 객체의 속성이나 배열을 불변으로 만드는 데 사용되는 키워드. 클래스의 속성이나 인터페이스의 속성을 변경할 수 없게 만든다.

 

- 객체의 속성에 readonly 사용하기

// Person 객체를 정의합니다.
interface Person {
  readonly name: string;
  age: number;
}

// Person 객체를 생성합니다.
const person: Person = { name: 'John', age: 30 };

// 읽기 전용 속성인 name의 값을 변경하려고 하면 오류가 발생합니다.
// person.name = 'Jane'; // 에러: Cannot assign to 'name' because it is a read-only property.

 

 

- 배열에 readonly 사용하기

// 숫자로 이루어진 배열을 정의합니다.
const numbers: readonly number[] = [1, 2, 3, 4];

// 읽기 전용 배열이므로 값을 변경하려고 하면 오류가 발생합니다.
// numbers.push(5); // 에러: Property 'push' does not exist on type 'readonly number[]'.
// numbers[0] = 0; // 에러: Index signature in type 'readonly number[]' only permits reading.

 

 

5. any

더보기

TypeScript에서 any 타입은 모든 타입의 슈퍼 타입으로, 어떤 타입의 값이든 저장할 수 있다. 하지만, 프로그램의 타입 안정성을 저해할수있기때문에 가급적 사용을 권장하지 않는다.

let anything: any;
anything = 5; // 숫자도 ok
anything = 'Hello'; // 문자열 ok
anything = { id: 1, name: 'John' }; // JSON도 ok

 

 

6. unknown

더보기

앞서말한 any와 비슷한 역할을 하지만 더 안전한 방식으로 동작한다. 모든 타입의 값을 저장하며, 만약다른 타입의 변수에 할당하려면 명시적으로 타입을 확인해야 한다. unknown 타입은 런타임에 어떤 값이 될지 알 수 없는 경우에 유용하게 활용된다. 다른 타입들과 마찬가지로 unknown에 대한 값에 대해 작업하기 위해서는 명시적인 타입 체크나 타입 단언이 필요하다.

// 예제 1: 함수에서 unknown 사용하기
function printMessage(message: unknown) {
  if (typeof message === 'string') {
    // message가 문자열인 경우에만 출력
    console.log(message.toUpperCase());
  } else {
    console.log('메시지는 문자열이 아닙니다.');
  }
}

printMessage('Hello, TypeScript!'); // 출력: HELLO, TYPESCRIPT!
printMessage(42); // 출력: 메시지는 문자열이 아닙니다.

// 예제 2: 변수에 unknown 할당하고 타입 단언 사용하기
let userInput: unknown = 'user input';
let userName: string;

// 타입 단언을 통해 unknown을 명시적으로 string으로 변환
userName = userInput as string;
console.log(userName.length); // 이제 userName은 string 타입이므로 문자열 길이에 접근 가능

// 예제 3: 타입 가드 활용하기
function isString(value: unknown): value is string {
  return typeof value === 'string';
}

if (isString(userInput)) {
  console.log(userInput.length); // 이제 userInput은 string 타입이므로 문자열 길이에 접근 가능
} else {
  console.log('userInput은 문자열이 아닙니다.');
}

 

 

 

7. union

더보기

TypeScript에서 두 개 이상의 타입을 허용하고자 할 때 사용된다. union을 사용하면 여러 타입 중 하나가 될 수 있는 변수, 매개변수 또는 반환 값 등을 표현할 수 있다.

 

// 예제 1: 문자열 또는 숫자를 받는 함수
function printId(id: string | number) {
  console.log(`ID: ${id}`);
}

printId('user123'); // 출력: ID: user123
printId(456); // 출력: ID: 456

// 예제 2: 유니언 타입을 활용한 변수 선언
let value: string | number;
value = 'TypeScript';
console.log(value.length); // 문자열 속성에 접근 가능

value = 123;
console.log(value.toFixed(2)); // 숫자 속성에 접근 가능

// 예제 3: 타입 가드를 사용한 조건부 동작
function processInput(input: string | number) {
  if (typeof input === 'string') {
    // 문자열인 경우
    console.log(input.toUpperCase());
  } else {
    // 숫자인 경우
    console.log(input.toFixed(2));
  }
}

processInput('Hello'); // 출력: HELLO
processInput(3.1415); // 출력: 3.14

 

string | number는 문자열이나 숫자 중 하나가 될 수 있는 타입을 나타낸다. 함수의 매개변수나 변수 등에 이러한 유니언 타입을 사용하면 여러 종류의 값을 다룰 때 유연성을 가질 수 있다. 조건부 동작을 수행할 때, typeof 연산자를 사용하거나 타입 가드를 활용하여 각 타입에 맞는 작업을 수행할 수 있다.

 

 

8. 함수

더보기
function addNumber (a:number,b:number):number {
	return a + b;
}

addNumber(3, 7); // 10

// 괄호 안 input 옆에 들어가는 것은 input의 대한 type
// 괄호 후에 작성하는 type은 output data에 대한 type

 

 

9. 리액트 컴포넌트

 

더보기

기본형식

import React from 'react'

const App:React.FC = () => {
  return (
    <div>App</div>
  )
}

export default App;

 

 

상황: 만약 App 컴포넌트의 data를 Store 컴포넌트 ui에 보여주려고 한다면

**rafce (arrow 함수로 컴포넌트 정의 스니펫)

//App.tsx
import React from 'react'


let data = {
	name:'누나네 식당',
    category: 'western',
    address:{
    	city: 'incheon',
        detail: 'somewhere',
        zipCode: 111132
    },
    menu: [
    {
    	name:"pasta",
        price:2000,
        category: "PASTA"
    },
    {
        name:"pizza",
        price:3000,
        category: "PIZZA"
 
    }
    ]
}

const App:React.FC = () => {
  return (
    <div>App</div>
  )
}

export default App;

 

import React from 'react'
import Store from './Store';


let data = {
	name:'누나네 식당',
    category: 'western',
    address:{
    	city: 'incheon',
        detail: 'somewhere',
        zipCode: 111132
    },
    menu: [
    {
    	name:"pasta",
        price:2000,
        category: "PASTA"
    },
    {
        name:"pizza",
        price:3000,
        category: "PIZZA"
 
    }
    ]
}

const App:React.FC = () => {
  return (
    <div>
      <Store info={data} /> //어떤 데이터를 보내는지도 type 명시해야함. 하지만 현재 data는 다양한 타입들의 데이터가 모인 한마디로 정의할 수 없는 타입이다. 따라서 우리가 data를 위한 타입을 새로 만들어줘야 한다.
    </div>
  )
}

export default App;

 

어떤 데이터를 보내는지도 type 명시해야함. 하지만 현재 data는 다양한 타입들의 데이터가 모인 한마디로 정의할 수 없는 타입이다. 따라서 우리가 data를 위한 타입을 새로 만들어줘야 한다.

 

10. type 새로 만들기

더보기

src > model > restaurant.ts 파일 만들기

 

타입을 새로만드는 방법은 총 두 가지. 둘의 큰 차이는 없지만, syntax가 조금 다르다.

 

1. type 

 

export type Restaurant = {
  name: string;
  category: string;
  address: {
    city: string;
    detail:string;
    zipCode:number;
  }
  menu: {
    name: string;
    price:number;
    category:string;
  }[]
}

// let data = {
// 	name:'누나네 식당',
//     category: 'western',
//     address:{
//     	city: 'incheon',
//         detail: 'somewhere',
//         zipCode: 111132
//     },
//     menu: [
//     {
//     	name:"pasta",
//         price:2000,
//         category: "PASTA"
//     },
//     {
//         name:"pizza",
//         price:3000,
//         category: "PIZZA"
//     }
//     ]
// }

 

 

 

이제 App.tsx 로 다시 가서, let으로 선언한 data를 아래와 같이 바꿔준다.

import React from 'react'
import Store from './Store';
import { Restaurant } from './model/restaurant';


let data:Restaurant = {
	name:'누나네 식당',
    category: 'western',
    address:{
    	city: 'incheon',
        detail: 'somewhere',
        zipCode: 111132
    },
    menu: [
    {
    	name:"pasta",
        price:2000,
        category: "PASTA"
    },
    {
        name:"pizza",
        price:3000,
        category: "PIZZA"
 
    }
    ]
}

const App:React.FC = () => {
  return (
    <div>
      <Store info={data} /> //어떤 데이터를 보내는지도 type 명시해야함. 하지만 현재 data는 다양한 타입들의 데이터가 모인 한마디로 정의할 수 없는 타입이다. 따라서 우리가 data를 위한 타입을 새로 만들어줘야 한다.
    </div>
  )
}

export default App;

 

 

 

여기서 멈추는것이 아니라, restaurant.ts 에서 Restaurant라는 타입 안에 또 타입을 만들 수도 있다. Restaurant 안의 address와 menu를 를 타입으로 만들어보자.

export type Restaurant = {
  name: string;
  category: string;
  address: Address;
  menu: Menu[];
}

export type Address = {
    city: string;
    detail:string;
    zipCode:number;
}

export type Menu = {
  name: string;
  price:number;
  category:string;
}

 

 

 

App.tsx 로 돌아와서, data를 useState에 넣는다.

import React, { useState } from 'react'
import Store from './Store';
import { Restaurant } from './model/restaurant';


let data:Restaurant = {
	name:'누나네 식당',
    category: 'western',
    address:{
    	city: 'incheon',
        detail: 'somewhere',
        zipCode: 111132
    },
    menu: [
    {
    	name:"pasta",
        price:2000,
        category: "PASTA"
    },
    {
        name:"pizza",
        price:3000,
        category: "PIZZA"
 
    }
    ]
}

const App:React.FC = () => {
  const [myRestaurant, SetMyRestaurant] = useState(data);
  return (
    <div>
      <Store myRestaurant={data} /> //어떤 데이터를 보내는지도 type 명시해야함. 하지만 현재 data는 다양한 타입들의 데이터가 모인 한마디로 정의할 수 없는 타입이다. 따라서 우리가 data를 위한 타입을 새로 만들어줘야 한다.
    </div>
  )
}

export default App;

 

 

useState도 타입을 명시해줘야 한다!

그럼 아래처럼 변한다. 이것을 Generic 이라고 부른다.

const [myRestaurant, SetMyRestaurant] = useState<Restaurant>(data);

 

import React, { useState } from 'react'
import Store from './Store';
import { Restaurant } from './model/restaurant';


let data:Restaurant = {
	name:'누나네 식당',
    category: 'western',
    address:{
    	city: 'incheon',
        detail: 'somewhere',
        zipCode: 111132
    },
    menu: [
    {
    	name:"pasta",
        price:2000,
        category: "PASTA"
    },
    {
        name:"pizza",
        price:3000,
        category: "PIZZA"
 
    }
    ]
}

const App:React.FC = () => {
  const [myRestaurant, SetMyRestaurant] = useState<Restaurant>(data);
  
  return (
    <div>
      <Store info={myRestaurant} /> //어떤 데이터를 보내는지도 type 명시해야함. 하지만 현재 data는 다양한 타입들의 데이터가 모인 한마디로 정의할 수 없는 타입이다. 따라서 우리가 data를 위한 타입을 새로 만들어줘야 한다.
    </div>
  )
}

export default App;

 

 

이제 App.tsx 에서 넘길건 다 넘기고, 데이터를 받는 Store.tsx 에서 타입표기를 해줘야 한다.

 

 

2. interface

 

//Store.tsx

import React from 'react'

const Store:React.FC = (props) => {
  return (
    <div>Store</div>
  )
}

export default Store

 

interface를 이용해 props 타입을 정해준다.

 

//Store.tsx

import React from 'react'
import { Restaurant } from './model/restaurant'

interface OwnProps {
  info:Restaurant
}

const Store:React.FC<OwnProps> = (props) => {
  return (
    <div>Store</div>
  )
}

export default Store

 

 

구조분해할당을 통해 props를 넘겨줄수도 있다. (아래참고)

//Store.tsx

import React from 'react'
import { Restaurant } from './model/restaurant'

interface OwnProps {
  info:Restaurant
}

const Store:React.FC<OwnProps> = ({info}) => {
  return (
    <div>Store</div>
  )
}

export default Store

 

 

ui 에 받아온 데이터 보여주기

import React from 'react'
import { Restaurant } from './model/restaurant'

interface OwnProps {
  info:Restaurant
}

const Store:React.FC<OwnProps> = ({info}) => {
  return (
    <div>{info.name}</div>
  )
}

export default Store

 

 

 

++ 함수도 보내보자!

식당의 주소를 바꾸는 함수를 만들기

  const changeAddress = (address: Address) => {
    SetMyRestaurant({...myRestaurant, address: address});
  }
  return (
    <div>
      <Store info={myRestaurant} changeAddress={changeAddress}/> 
      {/* 어떤 데이터를 보내는지도 type 명시해야함. 하지만 현재 data는 다양한 타입들의 데이터가 모인 한마디로 정의할 수 없는 타입이다. 따라서 우리가 data를 위한 타입을 새로 만들어줘야 한다. */}
    </div>
  )
// App.tsx

import React, { useState } from 'react'
import Store from './Store';
import { Address, Restaurant } from './model/restaurant';


let data:Restaurant = {
	name:'누나네 식당',
    category: 'western',
    address:{
    	city: 'incheon',
        detail: 'somewhere',
        zipCode: 111132
    },
    menu: [
    {
    	name:"pasta",
        price:2000,
        category: "PASTA"
    },
    {
        name:"pizza",
        price:3000,
        category: "PIZZA"
 
    }
    ]
}

const App:React.FC = () => {
  const [myRestaurant, SetMyRestaurant] = useState<Restaurant>(data);
  const changeAddress = (address: Address) => {
    SetMyRestaurant({...myRestaurant, address: address});
  }
  return (
    <div>
      <Store info={myRestaurant} changeAddress={changeAddress}/> 
      {/* 어떤 데이터를 보내는지도 type 명시해야함. 하지만 현재 data는 다양한 타입들의 데이터가 모인 한마디로 정의할 수 없는 타입이다. 따라서 우리가 data를 위한 타입을 새로 만들어줘야 한다. */}
    </div>
  )
}

export default App;

 

 

이제 Store.tsx 에서 받아온 데이터 타입 정해주기

//Store.tsx

import React from 'react'
import { Address, Restaurant } from './model/restaurant'

interface OwnProps {
  info:Restaurant,
  changeAddress(address:Address):void
  // return type이 없을경우 void 를 적고, 다른 타입이 있을경우 적어준다 예(boolean)
}

const Store:React.FC<OwnProps> = ({info}) => {
  return (
    <div>{info.name}</div>
  )
}

export default Store

 

 

 

11. extends

만약 restaurant의 Menu 타입에 다른 것들을 더 붙이고 싶을 때 사용한다.

1) interface안에서 extends

더보기

1) interface안에서 extends

best menu를 보여주는 컴포넌트를 새로 만들어준다.

src > BestMenu.tsx

import React from 'react'

const BestMenu:React.FC = () => {
  return (
    <div>BestMenu</div>
  )
}

export default BestMenu

 

 

App 에서 BestMenu 컴포넌트로 데이터를 넘겨준다.

import React, { useState } from 'react'
import Store from './Store';
import { Address, Restaurant } from './model/restaurant';
import BestMenu from './BestMenu';


let data:Restaurant = {
	name:'누나네 식당',
    category: 'western',
    address:{
    	city: 'incheon',
        detail: 'somewhere',
        zipCode: 111132
    },
    menu: [
    {
    	name:"pasta",
        price:2000,
        category: "PASTA"
    },
    {
        name:"pizza",
        price:3000,
        category: "PIZZA"
 
    }
    ]
}

const App:React.FC = () => {
  const [myRestaurant, SetMyRestaurant] = useState<Restaurant>(data);
  const changeAddress = (address: Address) => {
    SetMyRestaurant({...myRestaurant, address: address});
  }
  return (
    <div>
      <Store info={myRestaurant} changeAddress={changeAddress}/> 
      <BestMenu name="불고기피자" category="피자" price={1000}/>
    </div>
  )
}

export default App;

 

 

데이터를 받는 BestMenu.tsx 에서는 받는 데이터에 대한 타입을 interface로 정의하면 되는데, 우리가 넘겨받는 데이터의 타입은 이미 이전에 restuarant.ts에서 type으로 만들어놓았기 때문에! extends Menu {} 를 써준다. 그리고 이것 이외에 또 추가하고 싶은 것들을 {} 안에 넣어준다. 예를 들어 함수를 추가해준다고 가정해보자.

import React from 'react'
import { Menu } from './model/restaurant'

interface OwnProps extends Menu{
  // 추가하고싶은 것들만 적기
}

const BestMenu:React.FC<OwnProps> = () => {
  return (
    <div>BestMenu</div>
  )
}

export default BestMenu

 

 

App.tsx에서 아래함수를 추가해보자.

  const showBestMenuName = (name:string) => {
    return name
  }

 

잘 넘겨주고

      <BestMenu name="불고기피자" category="피자" price={1000} showBestMenuName={showBestMenuName}/>
import React, { useState } from 'react'
import Store from './Store';
import { Address, Restaurant } from './model/restaurant';
import BestMenu from './BestMenu';


let data:Restaurant = {
	name:'누나네 식당',
    category: 'western',
    address:{
    	city: 'incheon',
        detail: 'somewhere',
        zipCode: 111132
    },
    menu: [
    {
    	name:"pasta",
        price:2000,
        category: "PASTA"
    },
    {
        name:"pizza",
        price:3000,
        category: "PIZZA"
 
    }
    ]
}

const App:React.FC = () => {
  const [myRestaurant, SetMyRestaurant] = useState<Restaurant>(data);
  const changeAddress = (address: Address) => {
    SetMyRestaurant({...myRestaurant, address: address});
  }
  const showBestMenuName = (name:string) => {
    return name
  }
  return (
    <div>
      <Store info={myRestaurant} changeAddress={changeAddress}/> 
      <BestMenu name="불고기피자" category="피자" price={1000} showBestMenuName={showBestMenuName}/>
    </div>
  )
}

export default App;

 

 

BestMenu에서 잘 받아오자.

// Store.tsx

import React from 'react'
import { Menu } from './model/restaurant'

interface OwnProps extends Menu{
  showBestMenuName(name:string):string
}

const BestMenu:React.FC<OwnProps> = ({name, price, category, showBestMenuName}) => {
  return (
    <div>BestMenu</div>
  )
}

export default BestMenu

 

화면에 잘 그려주자

//BestMenu.tsx

import React from 'react'
import { Menu } from './model/restaurant'

interface OwnProps extends Menu{
  showBestMenuName(name:string):string
}

const BestMenu:React.FC<OwnProps> = ({name, price, category, showBestMenuName}) => {
  return (
    <div>
      <h1>Best Menu</h1>
      <p>{name}</p>
      <p>{price}</p>
      <p>{category}</p>
    </div>
  )
}

export default BestMenu

 

 

 

2) type안에서 extends

더보기

2) type안에서 extends

type OwnProps = menu & {
  showBestMenuName(name:string):string
}

 

 

그럼 type과 interface의 차이는 무엇인가요?

type에서는 omit 기능이 있다!

 

2-1) Omit

더보기

type에서 특정 요소를 제외하고 쓰고싶을 때!

 

예를 들어 Address의 타입에서 zipcode를 빼고 싶다고 하면

restuarnt.ts

export type AddressWithoutZipcode = Omit<Address, 'zipCode'>

 

 

상황 예 2) App 에서 BestMenu로 넘겨주는 데이터중 price를 빼고싶을 때

 

먼저 App 에서

import React, { useState } from 'react'
import Store from './Store';
import { Address, Restaurant } from './model/restaurant';
import BestMenu from './BestMenu';


let data:Restaurant = {
	name:'누나네 식당',
    category: 'western',
    address:{
    	city: 'incheon',
        detail: 'somewhere',
        zipCode: 111132
    },
    menu: [
    {
    	name:"pasta",
        price:2000,
        category: "PASTA"
    },
    {
        name:"pizza",
        price:3000,
        category: "PIZZA"
 
    }
    ]
}

const App:React.FC = () => {
  const [myRestaurant, SetMyRestaurant] = useState<Restaurant>(data);
  const changeAddress = (address: Address) => {
    SetMyRestaurant({...myRestaurant, address: address});
  }
  const showBestMenuName = (name:string) => {
    return name
  }
  return (
    <div>
      <Store info={myRestaurant} changeAddress={changeAddress}/> 
      <BestMenu name="불고기피자" category="피자" price={1000} showBestMenuName={showBestMenuName}/>
    </div>
  )
}

export default App;

 

price 빼기

      <BestMenu name="불고기피자" category="피자" showBestMenuName={showBestMenuName}/>

 

 

BestMenu 수정

// BestMenu.tsx

interface OwnProps extends Menu{
  showBestMenuName(name:string):string
}
interface OwnProps extends Omit<Menu, 'price'> {
  showBestMenuName(name:string):string
}

 

그 후, BestMenu 컴포넌트에서 구조분해로 받아온 price는 삭제

const BestMenu:React.FC<OwnProps> = ({name, price, category, showBestMenuName}) => {
  return (
    <div>
      <h1>Best Menu</h1>
      <p>{name}</p>
      <p>{price}</p>
      <p>{category}</p>
    </div>
  )
}
const BestMenu:React.FC<OwnProps> = ({name, category, showBestMenuName}) => {
  return (
    <div>
      <h1>Best Menu</h1>
      <p>{name}</p>
      <p>{price}</p>
      <p>{category}</p>
    </div>
  )
}

 

 

2-2) Pick

더보기

선택해서 사용하는 것

상황: Restaurant 타입에서 category만 가져오고 싶다!

 

// restaurant.ts

export type RestaurantOnlyCatetory = Pick<Restaurant, 'category'>

 

 

 

12. '?'

더보기

Omit과 비슷하게 쓰일 수 있는 방법으로

export type Address = {
    city: string;
    detail:string;
    zipCode?:number;
}

 

zipCode가 있을수도 있고, 없을 수도 있다는 뜻이다.

 

 

 

13. API 콜을 타입스크립트로 처리하기

더보기

제네릭을 이용하여 'T'라는 이름으로 input이 들어온다는 가정

export type ApiResponse<T> = {
  data: T[],
  totalPage: number,
  page: number
}
export type RestaurantResponse = ApiResponse<Restaurant> // data에서 Restaurant에 해당하는 값
export type MenuResponse = ApiResponse<Menu> // data에서 Menu에 해당하는 값

 

 

728x90
반응형