Skip to content

Latest commit

ย 

History

History
196 lines (162 loc) ยท 7.77 KB

2020-10-08_React_usingAPI.md

File metadata and controls

196 lines (162 loc) ยท 7.77 KB

React _ API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์™ธ๋ถ€ ๋ฐ์ดํ„ฐ ๋ฐ›์•„์˜ค๊ธฐ

1. DI (Dependency Injection)

diDiagram

DI๋ž€?

์˜์กด์„ฑ ์ฃผ์ž…์ด๋ผ๊ณ  ๋ถˆ๋ฆฌ๋ฉฐ, ์˜์กดํ•˜๋Š” ๊ฐ์ฒด๋ฅผ ๋ถ„๋ฆฌํ•˜์—ฌ ์ฃผ์ž…ํ•œ๋‹ค.
๊ธฐ์กด์— a์˜ ๋‚ด๋ถ€์— ์žˆ๋Š” b๋Š” c,e ๋“ฑ์— ๊ด€๊ณ„ ์˜์กด์„ฑ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.
ํ•˜์ง€๋งŒ, ์˜์กด์„ฑ์ฃผ์ž…์„ ์‚ฌ์šฉํ•˜์—ฌ b๋ฅผ a๋กœ ๋ถ€ํ„ฐ ๋ถ„๋ฆฌํ•˜์—ฌ ์ฃผ์ž…ํ•˜๋ฉด(์˜์กด๊ด€๊ณ„ ์—ญ์ „, ์˜์กด์„ฑ์˜ ๋ถ„๋ฆฌ) c,e๋Š” a์— ์ฃผ์ž…๋œ b์™€ ๊ด€๊ณ„์„ฑ์ด ํ˜•์„ฑ๋˜๋ฏ€๋กœ b์ž์ฒด ๋ณด๋‹ค๋Š” a์™€์˜ ๊ด€๊ณ„์— ์‹ ๊ฒฝ์“ฐ๋ฉด ๋œ๋‹ค.

DI์˜ ์žฅ์  (๊ธฐ๋Šฅ ๋‹จ์œ„๋กœ ๋ถ„๋ฆฌ๋œ๋‹ค)

  1. ํ…Œ์ŠคํŠธ๊ฐ€ ์šฉ์ดํ•˜๋‹ค
  2. ์ฝ”๋“œ์˜ ์žฌ์‚ฌ์šฉ์„ฑ์„ ๋†’์—ฌ์ค€๋‹ค
  3. ๊ฐ์ฒด ๊ฐ„ ๊ฒฐํ•ฉ๋„๋ฅผ ์ค„์—ฌ์ค€๋‹ค
  4. ๊ฐ€๋…์„ฑ์ด ์ข‹์•„์ง„๋‹ค

ํ™œ์šฉ

์™ธ๋ถ€์™€ ํ†ต์‹ ํ•˜๋Š” API๋ฅผ ์—ฐ๊ฒฐํ•  ๋•Œ, ๊ทธ ์—ญํ• ๋งŒ ์ˆ˜ํ–‰ํ•˜๋Š” ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ๋”ฐ๋กœ ๊ด€๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค. ๋”ฐ๋ผ์„œ, ์—ฌ๋Ÿฌ API๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜, ์ˆ˜์ •์‹œ ๋ถ„๋ฆฌ๋˜์–ด ์žˆ์–ด ์œ ์—ฐํ•œ ํ™œ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
์•„๋ž˜์˜ ์˜ˆ์‹œ์™€ ๊ฐ™์ด const youtube = new Youtube(); ์˜ ํ˜•์‹์œผ๋กœ ์ „๋‹ฌ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

2. Comparison - Fetch & Axios

XMLHttpRequest

XMLHttpRequest๋Š” fetch๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์ด์ „์— ์ฃผ๋กœ ์‚ฌ์šฉ๋˜์—ˆ๋˜ ๋ฐฉ์‹์ด๋ฉฐ, ์„œ๋ฒ„์™€ ์ƒํ˜ธ์ž‘์šฉ ํ•˜๊ธฐ์œ„ํ•ด ์‚ฌ์šฉ๋œ๋‹ค. XML๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋ชจ๋“  ํ˜•ํƒœ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ค๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ํ•˜์ง€๋งŒ, ๊ฐ๊ฐ์˜ ๋‹จ๊ฒŒ๋ณ„๋กœ ์–ด๋–ป๊ฒŒ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์˜ค๊ณ  ์ฒ˜๋ฆฌํ•ด์ค„ ๊ฑด์ง€ ์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜์œผ๋กœ ์„ค์ •ํ•˜์—ฌ ์ž‘์„ฑํ•˜์—ฌ์•ผ ํžŒ๋‹ค. MDN ๋ฌธ์„œ ์ฐธ๊ณ 

const xhr = new XMLHttpRequest();
xhr.open("GET", "http://domain/service");
// request state change event
xhr.onreadystatechange = function () {
  // request completed?
  if (xhr.readyState !== 4) return;
  if (xhr.status === 200) {
    // request successful - show response
    console.log(xhr.responseText);
  } else {
    // request error
    console.log("HTTP error", xhr.status, xhr.statusText);
  }
};
// start request
xhr.send();

์ด๋ฒคํŠธ ๊ธฐ๋ฐ˜์œผ๋กœ ์„ค์ •์„ ํ•ด์ค˜์•ผ ํ–ˆ๊ธฐ์— ๊ฐ€๋…์„ฑ์ด ์ข‹์ง€ ์•Š์•˜๊ณ , ๋‹จ๊ณ„๋ณ„๋กœ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ–ˆ๋‹ค. ๊ทธ์— ๋น„์— fetch๋Š” Promise๋กœ ๋ฐ˜ํ™˜ํ•˜์—ฌ then, catch์˜ ๋ฉ”์„œ๋“œ ์ฒด์ด๋‹์œผ๋กœ ๊ฐ„๋‹จํ•œ ์ž‘์„ฑ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ๋‹ค๋งŒ, ๋„คํŠธ์›Œํฌ ์ƒํƒœ์ฝ”๋“œ๊ฐ€ 404,500์ด๋”๋ผ๋„ resolve๋กœ ๋ฐ˜ํ™˜ํ•˜๋ฉฐ, ๋„คํŠธ์›Œํฌ ์žฅ์•  ๋ฐœ์ƒ์‹œ์—๋งŒ reject ๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค. XMLHttpRequest๋Š” ๊ฑฐ์˜ ์‚ฌ์šฉ๋˜์ง€ ์•Š์œผ๋ฉฐ ๊ธฐ๋ณธ ๋‚ด์žฅ ํ•จ์ˆ˜์ธ fetch()๊ฐ€ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ๋‹ค.

์•„๋ž˜์˜ ์˜ˆ์‹œ๋Š” ๊ธฐ๋ณธ ๋‚ด์žฅํ•จ์ˆ˜์ธ fetch์™€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ Axios ๋‘๊ฐ€์ง€ ๋ฐฉ๋ฒ•์œผ๋กœ youtube ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด App ์ปดํฌ๋„ŒํŠธ์— ํ”„๋กญ์œผ๋กœ ์ „๋‹ฌํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ์ž‘์„ฑํ•˜์˜€๋‹ค.
App ์ปดํฌ๋„ŒํŠธ์—์„œ url์„ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ์ง€๋งŒ, ๋ฐ›์•„์˜ค๋Š” ๋กœ์ง์„ ๋ถ„๋ฆฌํ•˜์—ฌ App์— ๋„ฃ์–ด์คŒ์œผ๋กœ์จ(DI) ๊ธฐ๋Šฅ๋‹จ์œ„๋กœ ๋ถ„๋ฆฌ๋œ ์ฝ”๋“œ์ž‘์„ฑ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

Fetch

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์˜ ๋‚ด์žฅํ•จ์ˆ˜๋กœ ์„œ๋ฒ„์— ๋ฐ์ดํ„ฐ๋ฅผ ํ˜ธ์ถœ, ์š”์ฒญํ•˜๋Š” ํ•จ์ˆ˜๋กœ ๋น„๋™๊ธฐ์ ์œผ๋กœ promise๋กœ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

src/service/youtube.js

class Youtube {
  constructor(key) {
    this.key = key;
    this.getRequestOptions = {
      method: "GET",
      redirect: "follow",
    };
  }
  //youtube Class ๋‚ด๋ถ€์— ๋น„๋™๊ธฐ ๋ฉ”์„œ๋“œ๋ฅผ ์ƒ์„ฑ
  //๊ฐ€์žฅ ์œ ๋ช…ํ•œ ๋™์˜์ƒ์„ ๋ฐ›์•„์˜ค๋Š” url
  async mostPopular() {
    const response = await fetch(
      `https://www.googleapis.com/youtube/v3/videos?part=snippet&chart=mostPopular&maxResults=25&key=${this.key}`,
      this.getRequestOptions
    );
    //response๋ฅผ json์œผ๋กœ ๋ณ€ํ™˜
    const result = await response.json();
    return result.items;
  }
  //๊ฒ€์ƒ‰ํ•œ ๋™์˜์ƒ์„ ๋ฐ›์•„์˜ค๋Š” url
  async search(query) {
    const response = await fetch(
      `https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=25&q=${query}&type=video&key=${this.key}`,
      this.getRequestOptions
    );
    const result = await response.json();
    return result.items.map((item) => ({ ...item, id: item.id.videoId }));
  }
}
export default Youtube;

src/index.js

//youtube ๊ฐ์ฒด ์ƒ์„ฑํ•˜์—ฌ App ์ปดํฌ๋„ŒํŠธ์— youtube prop์œผ๋กœ ์ „๋‹ฌ
//App์˜ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ youtube url์„ ๋ฐ›์•„์˜ฌ ์ˆ˜ ์žˆ๋‹ค.
const youtube = new Youtube(process.env.REACT_APP_YOUTUBE_API_KEY);
ReactDOM.render(
  <React.StrictMode>
    <App youtube={youtube} />
  </React.StrictMode>,
  document.getElementById("root")
);

Axios

API๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ params ๋‹จ์œ„๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ key : value์˜ ํ˜•ํƒœ๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์–ด ์ฝ”๋“œ ๊ฐ€๋…์„ฑ์ด ์ข‹๋‹ค.

src/service/youtube.js

class Youtube {
  //httpClient์œผ๋กœ ๊ณตํ†ต์ ์œผ๋กœ ์‚ฌ์šฉ๋˜๋Š” baseUrl, key์ •๋ณด๋ฅผ ๋ฐ›์•„์˜ด
  constructor(httpClient) {
    this.youtube = httpClient;
  }

  async mostPopular() {
    //๋น„๊ต https://www.googleapis.com/youtube/v3/videos?part=snippet&chart=mostPopular&maxResults=25&key=${this.key}
    const response = await this.youtube.get("videos", {
      params: {
        part: "snippet",
        chart: "mostPopular",
        maxResults: 25,
      },
    });
    return response.data.items;
  }
  async search(query) {
    //๋น„๊ต https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=25&q=${query}&type=video&key=${this.key}
    const response = await this.youtube.get("search", {
      params: {
        part: "snippet",
        maxResults: 25,
        q: query,
        type: "video",
      },
    });
    return response.data.items.map((item) => ({
      ...item,
      id: item.id.videoId,
    }));
  }
}
export default Youtube;

src/index.js

const httpClient = axios.create({
  baseURL: "https://www.googleapis.com/youtube/v3",
  params: { key: process.env.REACT_APP_YOUTUBE_API_KEY },
});
const youtube = new Youtube(httpClient);
ReactDOM.render(
  <React.StrictMode>
    <App youtube={youtube} />
  </React.StrictMode>,
  document.getElementById("root")
);
์ถ”๊ฐ€ ์ฐธ๊ณ ์‚ฌํ•ญ

๋‹ค๋ฅธ ๊ณณ์— ์‚ฌ์šฉ๋œ ์˜ˆ์‹œ์ง€๋งŒ DI์˜ ๋‹ค๋ฅธ ์˜ˆ์‹œ์—ฌ์„œ ์ฐธ๊ณ ์‚ผ์•„ ์ž‘์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค. ์•„๋ž˜์™€ ๊ฐ™์ด DI๋ฅผ ํ•ด์ค„๋•Œ, ์ธ์Šคํ„ด์Šค(imageUploader)๋งŒ prop์œผ๋กœ ๋„˜๊ฒจ์ค„ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ component์˜ ํ˜•ํƒœ๋„ prop์œผ๋กœ ์ „๋‹ฌ์ด ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. <ImageFileInput />์ด๋ผ๋Š” component๊ฐ€ ์‚ฌ์šฉ๋˜์–ด์•ผ ํ•  ๊ณณ์— ๊ธฐ๋ณธ prop๊ฐ’์œผ๋กœ imageUploader๋ผ๋Š” ํด๋ž˜์Šค๋ฅผ ์ฃผ์ž…์‹œํ‚จ <FileInput/> ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋„ฃ์–ด์ค„ ์ˆ˜ ์žˆ๋‹ค. ์ด๋•Œ const FileInput = <ImageFileInput imageUploader={imageUploader} />๋กœ ์ž‘์„ฑํ•˜์ง€ ์•Š๊ณ  prop๋ฅผ ๋ฐ›์•„ ๋„ฃ์–ด์ฃผ๋Š” ๊ฒƒ์€ ์ถ”ํ›„ FileInput์— ์ถ”๊ฐ€์ ์œผ๋กœ ํ• ๋‹น๋  prop์˜ ์ ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋„๋ก ํ™•์žฅ์„ฑ์„ ์œ„ํ•ด์„œ ์ž…๋‹ˆ๋‹ค. ๊ณต๋ถ€ํ•˜๋Š” ์ค‘์ด์ง€๋งŒ, ์ปดํฌ๋„ŒํŠธ ์ž์ฒด๋„ prop์œผ๋กœ ๋„˜๊ฒจ์ค„ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์„ ์ฒ˜์Œ ์•Œ๊ฒŒ๋˜์„œ ์ถ”๊ฐ€๋กœ ๋„ฃ์–ด๋ณด์•˜์Šต๋‹ˆ๋‹ค.

src/index.js

const authService = new AuthService();
//ImageUploader๋Š” ์ด๋ฏธ์ง€ ํŒŒ์ผ์„ ์—…๋กœ๋“œ ๊ฐ€๋Šฅํ•œ API๋ฅผ ์—ฐ๊ฒฐํ•œ ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค
const imageUploader = new ImageUploader();
//ํ™•์žฅ์„ฑ์„ ์œ„ํ•ด props๋กœ ๋ฐ›๊ณ  ์ „๋‹ฌํ•จ
const FileInput = (props) => (
  <ImageFileInput {...props} imageUploader={imageUploader} />
);

//component prop ์ธ๊ฒฝ์šฐ ๋ณดํ†ต ๋Œ€๋ฌธ์ž๋กœ ์ „๋‹ฌํ•จ
ReactDOM.render(
  <React.StrictMode>
    <App authService={authService} FileInput={FileInput} />
  </React.StrictMode>,
  document.getElementById("root")
);
References

๋“œ๋ฆผ์ฝ”๋”ฉ ๊ฐ•์˜ ๋‚ด์šฉ์„ ๋ฐ”ํƒ•์œผ๋กœ ์ž‘์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค.
DI์— ๋Œ€ํ•ด ์ฐธ๊ณ ํ•œ ๊ธ€
XMLHttpRequest ๊ด€๋ จ ์ฐธ๊ณ ํ•œ ๊ธ€
fetch MDN