<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>소소한 코딩일지</title>
    <link>https://u-pic-code.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Thu, 16 Jul 2026 20:43:53 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>U_pic</managingEditor>
    <item>
      <title>[Flutter] Future&amp;lt;void&amp;gt; 와 void</title>
      <link>https://u-pic-code.tistory.com/147</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 void와 Future &amp;lt;void&amp;gt;는 같지 않다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;void&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;img style=&quot;text-align: center; caret-color: transparent; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot; src=&quot;https://blog.kakaocdn.net/dn/cwGbiG/btrSnFKZYCo/2qmsV4CVEOad1ApGWdXR80/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1330&quot; data-origin-height=&quot;482&quot; data-filename=&quot;스크린샷 2022-11-30 오전 7.48.46.png&quot; /&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;void f()는 아무것도 반환하지 않는 함수이다. 아래 결과를 보면 순차적으로 실행되면서 myVoid() 함수의 완료는 보장하지 않고 비동기 함수를 실행시킨다. 그래서 결과적으로 myVoid2가 main2보다 늦게 등장하게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Future&amp;lt;void&amp;gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1422&quot; data-origin-height=&quot;498&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6Qkz2/btrSsnaR7UB/TWg1xDPeh4iyz0J3LbjcX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6Qkz2/btrSsnaR7UB/TWg1xDPeh4iyz0J3LbjcX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6Qkz2/btrSsnaR7UB/TWg1xDPeh4iyz0J3LbjcX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6Qkz2%2FbtrSsnaR7UB%2FTWg1xDPeh4iyz0J3LbjcX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1422&quot; height=&quot;498&quot; data-origin-width=&quot;1422&quot; data-origin-height=&quot;498&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Future &amp;lt;void&amp;gt; f()는 동일하게 아무것도 반환하지 않지만, 호출자가 async/await을 통해 함수의 완료 시점을 결정할 수 있다. 위와 동일하게 순차적으로 함수를 실행하지만 중간 비동기 함수 myVoid()에 async/await을 통해 함수의 완료를 보장하고, 그 이후에 다음 코드를 실행시켰다. 결과적으로 myVoid 1, myVoid2 출력 후에 main2를 출력하는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;void로 선언한 함수에 async/await을 사용하면 다음과 같이 에러가 뜬다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2978&quot; data-origin-height=&quot;2218&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bgQQMj/btrSr4JilWa/khJYSusD0nvAXkUZcmvz3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bgQQMj/btrSr4JilWa/khJYSusD0nvAXkUZcmvz3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bgQQMj/btrSr4JilWa/khJYSusD0nvAXkUZcmvz3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbgQQMj%2FbtrSr4JilWa%2FkhJYSusD0nvAXkUZcmvz3K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2978&quot; height=&quot;2218&quot; data-origin-width=&quot;2978&quot; data-origin-height=&quot;2218&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과적으로 비동기적인 코드를 짤 때는 Future를 잘 적어주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Flutter</category>
      <category>Future</category>
      <author>U_pic</author>
      <guid isPermaLink="true">https://u-pic-code.tistory.com/147</guid>
      <comments>https://u-pic-code.tistory.com/147#entry147comment</comments>
      <pubDate>Wed, 30 Nov 2022 08:02:54 +0900</pubDate>
    </item>
    <item>
      <title>[Flutter] Function Widget과 Class Widget</title>
      <link>https://u-pic-code.tistory.com/146</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;앱 개발을 하면서 반복적으로 사용되거나 내부 로직이 약간 차이가 있지만 큰 틀에서는 비슷한 형태의 위젯을 만들 때 하위 위젯으로 구분하여 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위젯을 선언하는 방법으로는 Widget을 return 하는 Function Widget과 클래스로 선언하는 Class Widget을 사용할 수 있다. 지금까지는 무분별하게 사용하고 있었던 것 같아 그동안 내가 왜 / 어떻게 사용했는지, 그리고 그 쓰임새를 정확하게 하기 위해 정리하는 시간을 가졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;지금 나는 어떻게 사용하고 있는가.&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Flutter로 앱개발으 하면서 양쪽 모두 사용해서 위젯을 개발해봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 나는 Row나 Column을 사용할 때 하위 컴포넌트로 인해 전체 컴포넌트가 복잡해지는 경우나, 같은 위젯이 반복되는 경우에 클래스 위젯 내부에서 함수 위젯으로 분리하여 사용하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 페이지 단위, 형태가 다양한 위젯(ex. Button, Input)의 경우에 클래스 위젯으로 선언하여 사용하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 두가지로 똑같은 화면을 만들어보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;두 위젯의 차이점&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;rebuild의 범위&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 위젯으로 다음과 같이 동일한 화면을 만들어보았고, Dart DevTools를 통해 내부 위젯 투리 구조를 확인해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Function Widget&lt;/p&gt;
&lt;pre id=&quot;code_1669676552627&quot; class=&quot;scala&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: _myHomePage(),
    );
  }
}

Widget _myHomePage() {
  return Container(
      child: Center(
    child: Text('Hello, Flutter!'),
  ));
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-11-29 오전 7.44.22.png&quot; data-origin-width=&quot;662&quot; data-origin-height=&quot;212&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cbNm7q/btrShH3dvGT/kawhlo3uIdrr5nvIRcpWyk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cbNm7q/btrShH3dvGT/kawhlo3uIdrr5nvIRcpWyk/img.png&quot; data-alt=&quot;Funtion Widget을 넣었을 때 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cbNm7q/btrShH3dvGT/kawhlo3uIdrr5nvIRcpWyk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcbNm7q%2FbtrShH3dvGT%2Fkawhlo3uIdrr5nvIRcpWyk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;662&quot; height=&quot;212&quot; data-filename=&quot;스크린샷 2022-11-29 오전 7.44.22.png&quot; data-origin-width=&quot;662&quot; data-origin-height=&quot;212&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Funtion Widget을 넣었을 때 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1669676552628&quot; class=&quot;scala&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
        child: Center(
      child: Text('Hello, Flutter!'),
    ));
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-11-29 오전 7.42.29.png&quot; data-origin-width=&quot;540&quot; data-origin-height=&quot;258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c2Z5ZS/btrSmvz9In0/1oW2L7fvqnYbFKVzTUhBS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c2Z5ZS/btrSmvz9In0/1oW2L7fvqnYbFKVzTUhBS1/img.png&quot; data-alt=&quot;Class Widget을 넣었을 때 구조&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c2Z5ZS/btrSmvz9In0/1oW2L7fvqnYbFKVzTUhBS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc2Z5ZS%2FbtrSmvz9In0%2F1oW2L7fvqnYbFKVzTUhBS1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;540&quot; height=&quot;258&quot; data-filename=&quot;스크린샷 2022-11-29 오전 7.42.29.png&quot; data-origin-width=&quot;540&quot; data-origin-height=&quot;258&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Class Widget을 넣었을 때 구조&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Class의 경우 ( 클래스 이름 &amp;gt; build 하는 위젯 ) 구조로 상위 위젯에 포함되어 있는 것을 볼 수 있고, 반면에 Function의 경우 return 하는 위젯 자체가 상위 위젯에 그대로 포함되는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;구조적으로는 약간의 차이가 있지만 문제가 없어 보인다. 하지만 리프레시되는 경우에 그 차이가 발생한다. Class로 선언된 위젯은 리프레시될 때 Flutter의 랜더링 방식에 의해 Class 내부의 변경 점들만 리프레시된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면 Function으로 만든 위젯의 경우, 리프레시될 때 함수가 다시 호출되기 때문에 구조를 처음부터 다시 그린다.&lt;br /&gt;클래스로 선언된 경우 위젯 트리가 인식하지만, Function으로 return 된 위젯은 Function으로 구분한 위젯이 상위 위젯 내부에 포함된 채로 인식된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Performance&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1번과 연결된 내용으로, 리프레시했을 때 rebuild가 발생하는 범위가 클수록 성능이 떨어질 가능성이 높다. 그래서 Function으로 만든 위젯이 성능이 좋지 않을 가능성이 높다. 그리고 작은 구조의 경우 문제가 되지 않지만 구조가 복잡해지는 경우, 그 차이는 더욱 심할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;Bugs&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Function을 통해 위젯 트리에 위젯을 넣게 되면 버그에 노출될 수 있고 일부 성능 최적화를 놓칠 수 있다. Function을 사용하면 버그가 발생할 수 있지만, Class의 경우보다 그런 문제에 노출되지 않는다는 것을 보장한다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Class를 이용하는 것이 Function 위젯보다 더 나은 점이 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Class로 위젯을 선언했을 때는, 위에서 다룬 내용처럼 &lt;b&gt;최적화되어 있는 성능&lt;/b&gt;이나 &lt;b&gt;DevTools에서 보다 명확하게 구분된 위젯을 확인&lt;/b&gt;할 수 있다는 점 외에도, &lt;b&gt;서로 다른 레이아웃 간 전환이 일어날 때 리소스를 정확하게 처리하는지 확인 가능&lt;/b&gt;하고, &lt;b&gt;핫 리로드&lt;/b&gt;가 제대로 작동하는지 확인할 수 있다. 그리고 우리가 &lt;b&gt;Error 메시지를 확인할 때도 위젯 이름을 통해 위치를 확인&lt;/b&gt;할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Function 위젯은 이전에 내가 사용하던 방식으로는 사용할 수는 없지만 단일 위젯 내에서 코드가 복잡해질 때, 코드를 간략하게 정리하는 경우에는 선택적으로 사용할 수 있을 것 같다는 생각을 해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Function 보다 Class 기반으로 위젯을 생성하는 것을 습관화하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/h4&gt;
&lt;figure id=&quot;og_1669683358985&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[Flutter] Widget &amp;amp; Helper Method&quot; data-og-description=&quot;flutter에서 모든 것은 위젯이다. 코드를 작성하다 보면 특정 위젯의 build 함수가 길어질 수 있고, 이걸 별도의 위젯(파일)로 분리할 필요가 있다.&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@leeeeeoy/Flutter-Widget-Helper-Method&quot; data-og-url=&quot;https://velog.io/@leeeeeoy/Flutter-Widget-Helper-Method&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/lp111/hyQJBOHl4N/uCLSx5ZgFgZKPyqetVWcpK/img.png?width=488&amp;amp;height=384&amp;amp;face=0_0_488_384,https://scrap.kakaocdn.net/dn/bK77D3/hyQJwfx22t/oiKhK3WctrTaeBCsYoVZ61/img.png?width=488&amp;amp;height=384&amp;amp;face=0_0_488_384,https://scrap.kakaocdn.net/dn/b9VkD6/hyQJydmdms/rlLNNYkPkkuqGV37LzJdPk/img.jpg?width=3024&amp;amp;height=4032&amp;amp;face=1127_1417_1523_1851&quot;&gt;&lt;a href=&quot;https://velog.io/@leeeeeoy/Flutter-Widget-Helper-Method&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@leeeeeoy/Flutter-Widget-Helper-Method&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/lp111/hyQJBOHl4N/uCLSx5ZgFgZKPyqetVWcpK/img.png?width=488&amp;amp;height=384&amp;amp;face=0_0_488_384,https://scrap.kakaocdn.net/dn/bK77D3/hyQJwfx22t/oiKhK3WctrTaeBCsYoVZ61/img.png?width=488&amp;amp;height=384&amp;amp;face=0_0_488_384,https://scrap.kakaocdn.net/dn/b9VkD6/hyQJydmdms/rlLNNYkPkkuqGV37LzJdPk/img.jpg?width=3024&amp;amp;height=4032&amp;amp;face=1127_1417_1523_1851');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[Flutter] Widget &amp;amp; Helper Method&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;flutter에서 모든 것은 위젯이다. 코드를 작성하다 보면 특정 위젯의 build 함수가 길어질 수 있고, 이걸 별도의 위젯(파일)로 분리할 필요가 있다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1669683399020&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Flutter) Widget 생성 Class vs Function&quot; data-og-description=&quot;Flutter에서 widget을 생성할 때, class를 이용하여 생성할 수도 있고, function으로 생성할 수도 있다. 모든 ui 컴포넌트는 widget이고, widget은 class를 이용하여 stateful, stateless widget으로 생성하는 것으로 &quot; data-og-host=&quot;dumbokim.tistory.com&quot; data-og-source-url=&quot;https://dumbokim.tistory.com/34&quot; data-og-url=&quot;https://dumbokim.tistory.com/34&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/c9Qino/hyQJuB2lQI/4wsKMh8HRFal1ktp3K2j2K/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/q0vXs/hyQJC08ptS/aCf5MzrfktObbujLC2UXX1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800&quot;&gt;&lt;a href=&quot;https://dumbokim.tistory.com/34&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://dumbokim.tistory.com/34&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/c9Qino/hyQJuB2lQI/4wsKMh8HRFal1ktp3K2j2K/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800,https://scrap.kakaocdn.net/dn/q0vXs/hyQJC08ptS/aCf5MzrfktObbujLC2UXX1/img.png?width=800&amp;amp;height=800&amp;amp;face=0_0_800_800');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Flutter) Widget 생성 Class vs Function&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Flutter에서 widget을 생성할 때, class를 이용하여 생성할 수도 있고, function으로 생성할 수도 있다. 모든 ui 컴포넌트는 widget이고, widget은 class를 이용하여 stateful, stateless widget으로 생성하는 것으로&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;dumbokim.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Flutter</category>
      <category>flutter</category>
      <author>U_pic</author>
      <guid isPermaLink="true">https://u-pic-code.tistory.com/146</guid>
      <comments>https://u-pic-code.tistory.com/146#entry146comment</comments>
      <pubDate>Tue, 29 Nov 2022 09:57:04 +0900</pubDate>
    </item>
    <item>
      <title>&amp;lt;div&amp;gt;, &amp;lt;&amp;gt;, React.Fragment는 뭐가 다를까?</title>
      <link>https://u-pic-code.tistory.com/145</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;1602&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/d9824l/btrO0oEvHsd/kChZJ2CxlrU8nUw5K9rKok/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/d9824l/btrO0oEvHsd/kChZJ2CxlrU8nUw5K9rKok/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/d9824l/btrO0oEvHsd/kChZJ2CxlrU8nUw5K9rKok/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fd9824l%2FbtrO0oEvHsd%2FkChZJ2CxlrU8nUw5K9rKok%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;618&quot; height=&quot;330&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;1602&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Atomic Design 관련해서 기술 블로그를 보다가 원자 단위의 컴포넌트를 합치는 과정에서 &amp;lt;div&amp;gt; 태그로 감싸는 것을 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 코드를 보고, `음, 빈 태그(&amp;lt;&amp;gt;&amp;lt;/&amp;gt;)로 감싸면 안 되나? 의미상 차이가 큰가?`라는 생각을 하게 되었다. 그리고 '빈 태그가 React.Fragment와 같다고 했던 거 같은데' 하는 생각이 연달아 들었기에 호다닥 &lt;b&gt;&amp;lt;div&amp;gt;, &amp;lt;&amp;gt;, React.Fragment&lt;/b&gt;를 살짝 정리해보고 왜 &amp;lt;div&amp;gt;로 감싸게 되었는지 알아보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&amp;lt;div&amp;gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;div 태그는 컨텐츠를 담는 컨테이너로서 쓰인다. 콘텐츠가 보일 영역을 구분하는 태그이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;React.Fragment&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React.Fragment는 컴포넌트가 새로운 노드를 추가하지 않고 여러 자식을 그룹화할 수 있게 해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 문서의 설명을 참고하면,&lt;/p&gt;
&lt;pre id=&quot;code_1666102980231&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Table extends React.Component {
  render() {
    return (
      &amp;lt;table&amp;gt;
        &amp;lt;tr&amp;gt;
          &amp;lt;Columns /&amp;gt;
        &amp;lt;/tr&amp;gt;
      &amp;lt;/table&amp;gt;
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블 태그 아래 &amp;lt;Colums /&amp;gt; 컴포넌트는 여러 개의 &amp;lt;td /&amp;gt; 태그를 반환해주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 일반적으로 React 컴포넌트는 1개의 자식만을 반환할 수 있기 때문에 다음과 예시처럼 &amp;lt;div&amp;gt; 태그와 같이 다른 태그로 감싸서 return 해주는 것이 일반적이다.&lt;/p&gt;
&lt;pre id=&quot;code_1666103119937&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Columns extends React.Component {
  render() {
    return (
      &amp;lt;div&amp;gt;
        &amp;lt;td&amp;gt;Hello&amp;lt;/td&amp;gt;
        &amp;lt;td&amp;gt;World&amp;lt;/td&amp;gt;
      &amp;lt;/div&amp;gt;
    );
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이렇게 되면 &amp;lt;tr&amp;gt; 태그 아래에 &amp;lt;div&amp;gt; 태그가 포함되는 형태가 되기 때문에 적절한 형태라고 볼 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 형태를 유지하기 위해 React.Fragment를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React.Fragment의 축약 문법으로 &amp;lt;&amp;gt;&amp;lt;/&amp;gt;를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;축약 문법(&amp;lt;&amp;gt;&amp;lt;/&amp;gt;)을 사용하는 경우 Fragments에 key를 사용할 수 없기 때문에 상황에 맞는 문법을 사용하는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;결론&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 다시 돌아와서,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 궁금했던 것은 Atomic Design에서 &lt;u&gt;&lt;b&gt;원자화되어있는 UI를 조합하여 사용하는 경우에 어떤 태그를 사용하는 것이 적합 한가였다.&lt;/b&gt;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div&gt;&amp;lt;div&amp;gt; 태그와 React.Fragments를 비교해 보았을 때,&lt;/div&gt;
&lt;div&gt;조합하는 UI를 상위 컴포넌트에 개별로 안착시키는 것이 아니라 하나의 UI로서 사용될 것이기 때문에 컨텐츠의 영역을 의미하는 &amp;lt;div&amp;gt; 태그로 묶어서 사용하는 것이 더 적절하다는 결론이 났다.&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Vue&amp;amp;React</category>
      <category>React</category>
      <author>U_pic</author>
      <guid isPermaLink="true">https://u-pic-code.tistory.com/145</guid>
      <comments>https://u-pic-code.tistory.com/145#entry145comment</comments>
      <pubDate>Tue, 18 Oct 2022 23:36:19 +0900</pubDate>
    </item>
    <item>
      <title>Axios Interceptors 적용해보기</title>
      <link>https://u-pic-code.tistory.com/144</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-09-23 오전 3.37.14.png&quot; data-origin-width=&quot;2582&quot; data-origin-height=&quot;1414&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w9tSn/btrMOV5wRSl/tPPUZD1qPyZ1KGqS9MS9LK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w9tSn/btrMOV5wRSl/tPPUZD1qPyZ1KGqS9MS9LK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w9tSn/btrMOV5wRSl/tPPUZD1qPyZ1KGqS9MS9LK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw9tSn%2FbtrMOV5wRSl%2FtPPUZD1qPyZ1KGqS9MS9LK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;639&quot; height=&quot;350&quot; data-filename=&quot;스크린샷 2022-09-23 오전 3.37.14.png&quot; data-origin-width=&quot;2582&quot; data-origin-height=&quot;1414&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;API 로직을 작성하면서 단순하게 요청, 응답 데이터 처리, 에러 메시지 전달 로직을 작성하는 것에 그쳤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 Axios Interceptors로 기존의 API 로직을 수정해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Axios Interceptors&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Axios Interceptors는 interceptor라는 단어의 의미에 알맞게 API 요청/응답을 주고받을 때,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청 전/ 요청 후에 각 동작을 가로채서 특정 전처리를 해준다.&lt;/p&gt;
&lt;pre id=&quot;code_1663872810995&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 요청 인터셉터 추가
axios.interceptors.request.use(
  function (config) {
    // 요청을 보내기 전에 수행할 일
    // ...
    return config;
  },
  function (error) {
    // 오류 요청을 보내기전 수행할 일
    // ...
    return Promise.reject(error);
  });

// 응답 인터셉터 추가
axios.interceptors.response.use(
  function (response) {
    // 응답 데이터를 가공
    // ...
    return response;
  },
  function (error) {
    // 오류 응답을 처리
    // ...
    return Promise.reject(error);
  });&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용 전에 공식문서와 블로그를 참고하여 다양한 개선사항을 반영할 수 있을 것 같다는 생각이 들어 적용하기로 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 이번 Axios Interceptors로 하고자 하는 것은 다음과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. BaseURl 지정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. header 및 토큰 지정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. Error Handling&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;개선 후 코드&lt;/b&gt;&lt;/h4&gt;
&lt;pre id=&quot;code_1663872992331&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import axios from 'axios';

export const apiInstance = axios.create({
  baseURL: process.env.NODE_ENV === 'development' ? 'http://localhost:1024' : 'http://saaipeer.com',
  timeout: 5000,
});

apiInstance.interceptors.request.use(
  (config) =&amp;gt; {
    if (config.url.includes('evaluation-papers') &amp;amp;&amp;amp; !config.url.includes('students')) {
      const token = document.cookie.match('(^|;) ?token=([^;]*)(;|$)');
      config.headers.Authorization = token[2];
    }
    return config;
  },
  (error) =&amp;gt; {
    return Promise.reject(error);
  }
);

apiInstance.interceptors.response.use(
  function (response) {
    if (response.config.url.includes('token')) {
      const token = response.data.token;
      const date = new Date();
      date.setTime(date.getTime() + 1000 * 60 * 60);
      document.cookie = 'token=' + token + ';expires=' + date.toUTCString() + ';path=/';
    }

    return response.data;
  },
  function (error) {
    return Promise.reject(error);
  }
);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개선 후에는 다음과 같은 이점을 얻을 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1. 동일 axios instance&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 모든 로직에서 axios.~으로 요청을 보내는 것은 보낼 때마다 axios instance를 새롭게 생성하는 방법이다. 초기에 한 개의 axios instance를 만들고, 이 instance를 호출하여 사용하도록 하였다. 이때 axios instance를 선언하는 과정에서 baseURL과 timer 프로퍼티를 지정해줌으로써 일괄적으로 모든 요청에 적용 가능했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. token 관리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 로직에서는 토큰이 필요한 로직 내에서 토큰 정보를 확인하고, 가져오는 메서드를 따로 만들어 내부에서 사용하도록 했다. 함수를 선언할 때, 한 가지의 동작만 하도록 하는 것이 더 명확한 함수 정의 방식이라고 생각해 기존 API 요청 로직 내부에 있는 토큰 관련 로직을 Interceptor에서 관리하여 API 요청 로직은 더욱 요청에 집중한 로직으로서 관심사를 분리시킬 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;3. Error Handler&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청 시, 응답 에러에 대해서 일괄적으로 에러를 적용시킬 수 있다. 이 장점의 경우 위 로직에서는 에러 객체를 넘겨주는 것으로 충분하다고 생각하여 추가로 코드를 덧붙이지 않았다. 이후 에러 status 별로 다른 처리를 해줄 필요가 있을 경우 여기 영역에서 처리해줄 수 있어 API 요청 코드 영역에서 코드량을 확실히 줄일 수 있을 것 같다는 생각을 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;Reference&lt;/b&gt;&lt;/h4&gt;
&lt;figure id=&quot;og_1663872056999&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;인터셉터 |&amp;nbsp;Axios Docs&quot; data-og-description=&quot;인터셉터 then 또는 catch로 처리되기 전에 요청과 응답을 가로챌수 있습니다. axios.interceptors.request.use(function (config) { return config; }, function (error) { return Promise.reject(error); }); axios.interceptors.response.use(f&quot; data-og-host=&quot;axios-http.com&quot; data-og-source-url=&quot;https://axios-http.com/kr/docs/interceptors&quot; data-og-url=&quot;https://axios-http.com/kr/docs/interceptors&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://axios-http.com/kr/docs/interceptors&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://axios-http.com/kr/docs/interceptors&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;인터셉터 |&amp;nbsp;Axios Docs&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;인터셉터 then 또는 catch로 처리되기 전에 요청과 응답을 가로챌수 있습니다. axios.interceptors.request.use(function (config) { return config; }, function (error) { return Promise.reject(error); }); axios.interceptors.response.use(f&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;axios-http.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1663874649797&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;axios interceptor 적용하기&quot; data-og-description=&quot;interceptor를 왜 적용했냐? 답은 바로 귀찮아서!!!!!!!!!!!!!!! 게다가 연구 초창기에 테스트를 위해서 주먹구구 사용하던 코드로 계속 개발을 진행하다 보니 아래와 같은 문제점이 있었음! 1) api 호출&quot; data-og-host=&quot;velog.io&quot; data-og-source-url=&quot;https://velog.io/@subanggu/axios-interceptor-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot; data-og-url=&quot;https://velog.io/@subanggu/axios-interceptor-적용하기&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cmvPB4/hyPSZpCHwf/HXO4fnINLbyDxmkX3Ubl6k/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500&quot;&gt;&lt;a href=&quot;https://velog.io/@subanggu/axios-interceptor-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://velog.io/@subanggu/axios-interceptor-%EC%A0%81%EC%9A%A9%ED%95%98%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cmvPB4/hyPSZpCHwf/HXO4fnINLbyDxmkX3Ubl6k/img.png?width=950&amp;amp;height=500&amp;amp;face=0_0_950_500');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;axios interceptor 적용하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;interceptor를 왜 적용했냐? 답은 바로 귀찮아서!!!!!!!!!!!!!!! 게다가 연구 초창기에 테스트를 위해서 주먹구구 사용하던 코드로 계속 개발을 진행하다 보니 아래와 같은 문제점이 있었음! 1) api 호출&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;velog.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Vue&amp;amp;React</category>
      <category>AxiosInterceptor</category>
      <author>U_pic</author>
      <guid isPermaLink="true">https://u-pic-code.tistory.com/144</guid>
      <comments>https://u-pic-code.tistory.com/144#entry144comment</comments>
      <pubDate>Fri, 23 Sep 2022 04:26:49 +0900</pubDate>
    </item>
    <item>
      <title>SPA와 CSR 그리고 SSR</title>
      <link>https://u-pic-code.tistory.com/143</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2149&quot; data-origin-height=&quot;1209&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cDzsaz/btrMkfEzgW0/vwDlUSCchOXUJKLSqAW1P0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cDzsaz/btrMkfEzgW0/vwDlUSCchOXUJKLSqAW1P0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cDzsaz/btrMkfEzgW0/vwDlUSCchOXUJKLSqAW1P0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcDzsaz%2FbtrMkfEzgW0%2FvwDlUSCchOXUJKLSqAW1P0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;507&quot; height=&quot;285&quot; data-filename=&quot;blob&quot; data-origin-width=&quot;2149&quot; data-origin-height=&quot;1209&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웹 개발을 하면서 가장 많이 사용하는 라이브러리, 프레임워크는 React, Vue.js, Angular이다. 이것들로 우리는 SPA를 만들 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SPA를 검색해보면 CSR, SSR이라는 용어들도 함께 등장한다. 이 세 가지 용어가 어떤 것을 의미하고, 서로 어떤 관계를 가지고 있고, 영향을 줄 수 있는지 알아보려고 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;SPA (Single Page Application)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SPA는 웹사이트 전체를 하나의 페이지(Single Page)에서 보여주고, 내부 요소를 바꾸면서 화면을 보여주는 페이지이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 옛날 웹사이트에서는 새로운 페이지로 전환될 때 마다 서버로부터 리소스를 전송받았다. 하지만 점차 페이지의 용량이 커지면서 서버에서 매번 리소스를 받아오는 부담이 커지게 되었고, 이 문제를 해결하기 위해 SPA가 등장하게 되었다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;CSR (Client Side Rendering)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에서는 최소한의 화면 구조 HTML만 랜더링하고, 나머지 데이터, 컴포넌트와 관련된 로직은 모두 브라우저(클라이언트)에서 실행되는 렌더링 방식이다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;자연스러운 사용자 경험(UX)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;CSR을 통해 만든 SPA는 초기에 서버로 부터 웹페이지에 대한 리소스를 모두 받아오기 때문에 이후 데이터 조회 이외에는 서버 통신이 없기 때문에 새로고침 없이 페이지를 탐색할 수 있다. 그리고 페이지간 라우팅 속도가 빠르기 때문에 페이자의 반응속도가 빨라 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서버와 클라이언트의 분리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트 부분 코드와 서버 코드의 영역을 조금더 명확하게 구분하여 개발, 관리 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;검색 최적화 (SEO)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SEO는 브라우저의 서버 랜더링에 대해서 웹 크롤링을 수행하여 관련 콘텐츠를 선정하는데 CSR의 경우 초기 서버 랜더링 요소가 거의 없고, 초기 랜더링 시간, 데이터 요청과 같이 의미 있는 콘텐츠가 화면에&lt;span&gt;&amp;nbsp; &lt;/span&gt;처음 렌더링 되는 시간이 길기 때문에 검색 최적화 부분에서 취약점을 가지고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;성능&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버로 부터 초기에 모든 리소를 받아서 사용하기 때문에 JS의 번들 크기가 크거나, 성능이 부족할 경우 초기에 렉이 걸리는 것처럼 부자연스러운 경험을 제공할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;데이터 조회&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 데이터를 이벤트를 통해 요청하고, 조회해야한다. 이때 데이터 요청 시 일정 시간이 필요하기 때문에 데이터가 없는 경우에 대한 클라이언트 쪽에서 대처가 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;SSR (Server Side Rendering)&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSR은 과거부터 사용되었던 웹페이지를 렌더링하는 방법이다. 사용자가 웹페이지를 요청했을 때 서버에서 전체 HTML 구조를 만들어 클라이언터로 전달한다. 이때 HTML에 필요한 데이터를 모두 포함하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;장점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;빠른 초기 랜더링&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 서버 측에서 화면을 랜더링하고, 데이터 요청, 조회를 담당하기 때문에 클라이언트의 JS 코드가 확연히 줄어든다. 그래서 초기 화면 랜더링 시 해석하고 로드하는 JS 코드라인이 적기 때문에 빠른 *FCP와 *TTI를 가진다.&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;클라이언트의 여유&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 말한 것 처럼 클라이언트에서는 데이터 조회, 랜더링을 담당하는 코드들이 제거되기 때문에 비교적 클라이언트 영역에서 다른 서드 파티 JS 라이브러리를 위한 공간을 많이 확보할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;검색 최적화 (SEO)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SEO는 구글 검색, 네이버 검색과 같이 검색엔진이 홈페이지의 구조와 콘텐츠를 잘 이해해서 우리 콘텐츠가 검색 결과를 상위에 노출시키기 위해 하는 작업을 말한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSR랜더링을 사용하면 검색엔진이 웹페이지의 구조와 컨텐츠를 쉽게 크롤링하여 파악할 수 있기 때문에 SEO의 수준이 높아진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;단점&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;서버의 부하&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSR의 웹 서버에서는 데이터 조회, 페이지 랜더링이 모두 서버에서 처리되므로 CSR보다 상대적으로 서버의 부하가 많다. 그리고 네트워크 환경이나, 사용자의 수, 서버의 환경 등에 영향을 받을 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;전체 페이지 새로고침&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSR은 페이지가 전환될 때마다 서버에 페이지 요청을 보낸다. 앞서 말한 서버의 부하 때문에 화면이 늦게 표시될 수 있는 가능성이 있고, 화면 전환시 화면 전체가 사라졌다가 다시 화면이 표현되어 깜빡이는 것처럼 보여 CSR에 비해 상대적으로 부자연스러운 UX를 제공할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;내가 고민한 부분&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;SPA는 CSR인가?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다양한 자료를 읽으면서 점차 SPA === CSR이라는 생각을 하고있게 되었다. 하지만 이것은 틀렸다. SPA라는 것을 화면 동작 방식 결과를 구현하기 위해 CSR이라는 방식을 채택한 것이다. 각각에 대해서 설명하다 보면 주체만 다르고 같은 설명을 하게 되는데 두 가지는 다르다는 설명을 먼저 명심하고 설명을 보아야 나중에 헷갈리지 않을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;SPA에서 SSR가 필요한 이유&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 생각하는 SPA에서 SSR을 적용하려는 이유는 크게 두가지 일 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;초기화면 랜더링 속도&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SPA는 CSR 방식(?)이기 때문에 웹 페이지에 대한 모든 리소스를 초기에 다운로드한다. 그래서 처음 화면을 랜더링 하는 시간이 길어질 가능성이 있고, 부자연스러운 사용자 경험을 제공할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;검색 최적화(SEO)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 엔진에 우리의 컨텐츠가 상위에 노출되는 것은 아주 중요하다. 상위에 노출될수록 사용자의 유입이 많아질 것이고 결국 매출로 이어질 수 있다. 앞서 CSR을 채택하는 SPA는 이러한 부분에서 취약점을 가지고 있기 때문에 아무리 자연스러운 사용자 경험을 제공하더라도 사용자가 들어오지 않으면 말짱 꽝이지 않을까.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;용어&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FCP (First Contentful Paint) : 페이지가 로드되기 시작하고 컨텐츠의 일부가 화면에 랜더링 될 때까지의 시간&lt;/li&gt;
&lt;li&gt;TTI (Time To Interactive) : JS의 실행이 완료되어, 페이지의 상호작용이 가능하게 될 때까지의 시점&lt;/li&gt;
&lt;li&gt;TTFB (Time to First Byte) : &lt;span&gt;사용자가&lt;/span&gt; &lt;span&gt;웹페이지를&lt;/span&gt; &lt;span&gt;요청하고&lt;/span&gt; &lt;span&gt;웹서버에서&lt;/span&gt; &lt;span&gt;전달받는&lt;/span&gt; &lt;span&gt;첫&lt;/span&gt; &lt;span&gt;바이트&lt;/span&gt;(Byte)&lt;span&gt;가&lt;/span&gt; &lt;span&gt;도착하는&lt;/span&gt; &lt;span&gt;시점&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;&lt;span&gt;Reference&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt;&lt;/span&gt;&lt;/b&gt;&lt;b&gt;&lt;span&gt;&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;
&lt;figure id=&quot;og_1663480514606&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;SPA(Single Page Application) 이란?&quot; data-og-description=&quot;너무 당연하게 사용해 온 SPA. 어떻게 작동하는 것일까요?&quot; data-og-host=&quot;www.huskyhoochu.com&quot; data-og-source-url=&quot;https://www.huskyhoochu.com/what-is-spa/&quot; data-og-url=&quot;https://www.huskyhoochu.com/what-is-spa/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/gjWFX/hyPQnpwsC5/G6IuyQF6Rkf7jvyVGZDf3k/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/bObegk/hyPOFZD7Tn/HlMIXn28uy2cMa6XLp6KGk/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512&quot;&gt;&lt;a href=&quot;https://www.huskyhoochu.com/what-is-spa/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.huskyhoochu.com/what-is-spa/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/gjWFX/hyPQnpwsC5/G6IuyQF6Rkf7jvyVGZDf3k/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512,https://scrap.kakaocdn.net/dn/bObegk/hyPOFZD7Tn/HlMIXn28uy2cMa6XLp6KGk/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;SPA(Single Page Application) 이란?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;너무 당연하게 사용해 온 SPA. 어떻게 작동하는 것일까요?&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.huskyhoochu.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1663480487990&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;클라이언트 사이드 렌더링&quot; data-og-description=&quot;클라이언트에서 앱의 UI를 렌더링한다 - Client-Side Rendering 클라이언트 사이드 렌더링에서 서버는 뼈대가 되는 최소한의 HTML&amp;hellip;&quot; data-og-host=&quot;patterns-dev-kr.github.io&quot; data-og-source-url=&quot;https://patterns-dev-kr.github.io/rendering-patterns/client-side-rendering/&quot; data-og-url=&quot;https://patterns-dev-kr.github.io/rendering-patterns/client-side-rendering/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dgjGh4/hyPQsK9vog/eeH6R1bzKTQdp6QCp1DvQ1/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/bqW0uE/hyPOMj8ITX/cQhXyAgvshx5i6d5COmsn1/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/boMTfk/hyPQdmU0Oc/9UyEmlHUAitRs1ZYQhC2Fk/img.png?width=1366&amp;amp;height=657&amp;amp;face=0_0_1366_657&quot;&gt;&lt;a href=&quot;https://patterns-dev-kr.github.io/rendering-patterns/client-side-rendering/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://patterns-dev-kr.github.io/rendering-patterns/client-side-rendering/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dgjGh4/hyPQsK9vog/eeH6R1bzKTQdp6QCp1DvQ1/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/bqW0uE/hyPOMj8ITX/cQhXyAgvshx5i6d5COmsn1/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/boMTfk/hyPQdmU0Oc/9UyEmlHUAitRs1ZYQhC2Fk/img.png?width=1366&amp;amp;height=657&amp;amp;face=0_0_1366_657');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;클라이언트 사이드 렌더링&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;클라이언트에서 앱의 UI를 렌더링한다 - Client-Side Rendering 클라이언트 사이드 렌더링에서 서버는 뼈대가 되는 최소한의 HTML&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;patterns-dev-kr.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1663480496192&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;서버 사이드 렌더링&quot; data-og-description=&quot;서버에서 렌더링한 HTML을 직접 응답한다 - 서버 사이드 렌더링은 웹 컨텐츠의 렌더링에 꽤 오랫동안 사용된 방법이다. 서버사이드 렌더링에서는 사용자의 페이지 요청에 대해 전체 HTML&amp;hellip;&quot; data-og-host=&quot;patterns-dev-kr.github.io&quot; data-og-source-url=&quot;https://patterns-dev-kr.github.io/rendering-patterns/server-side-rendering/&quot; data-og-url=&quot;https://patterns-dev-kr.github.io/rendering-patterns/server-side-rendering/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/culc89/hyPQsj4L47/KIC1x45dnYSlFNADssNiO1/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/MNBUO/hyPQhbL5qI/6DyV3gekkYDe12sP3dD0E0/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/ikBVA/hyPQnb0icj/xY1kJ6GykBWBmLSZBNTPZ1/img.jpg?width=994&amp;amp;height=614&amp;amp;face=0_0_994_614&quot;&gt;&lt;a href=&quot;https://patterns-dev-kr.github.io/rendering-patterns/server-side-rendering/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://patterns-dev-kr.github.io/rendering-patterns/server-side-rendering/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/culc89/hyPQsj4L47/KIC1x45dnYSlFNADssNiO1/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/MNBUO/hyPQhbL5qI/6DyV3gekkYDe12sP3dD0E0/img.jpg?width=1920&amp;amp;height=1080&amp;amp;face=0_0_1920_1080,https://scrap.kakaocdn.net/dn/ikBVA/hyPQnb0icj/xY1kJ6GykBWBmLSZBNTPZ1/img.jpg?width=994&amp;amp;height=614&amp;amp;face=0_0_994_614');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;서버 사이드 렌더링&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;서버에서 렌더링한 HTML을 직접 응답한다 - 서버 사이드 렌더링은 웹 컨텐츠의 렌더링에 꽤 오랫동안 사용된 방법이다. 서버사이드 렌더링에서는 사용자의 페이지 요청에 대해 전체 HTML&amp;hellip;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;patterns-dev-kr.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>지나가는 개념 정리</category>
      <category>csr</category>
      <category>SPA</category>
      <category>SSR</category>
      <author>U_pic</author>
      <guid isPermaLink="true">https://u-pic-code.tistory.com/143</guid>
      <comments>https://u-pic-code.tistory.com/143#entry143comment</comments>
      <pubDate>Sun, 18 Sep 2022 15:03:14 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.js] computed와 method</title>
      <link>https://u-pic-code.tistory.com/142</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Vue의 computed와 method의 차이를 스스로 명확하게 설명하지 못하고 있다고 생각해서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식문서와 블로그를 보면서 차이점을 정리해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 큰 차이는 &lt;b&gt;함수 연산 값을 사용하는지(computed)&lt;/b&gt; 또는 &lt;b&gt;항상 함수를 실행하는지(method)&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;computed의 경우, 템플릿의 data(종속된 값)에 따라 메서드의 연산 결과 값을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;종속된 값의 변경을 확인하고, 변경되지 않았을 경우에는 캐싱되어있는 값을 그대로 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;method의 경우, 화면이 랜더링되어서 메서드를 호출할 때마다 연산을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 VS 코드에서 computed옵션을 타고 computedOptions를 보니 cache 여부를 boolean으로 판단하고 있는 것을 확인할 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가) 전에 공부하면서 computed는 인자를 사용하지 않고, method는 인자를 사용한다라는 문장을 본 기억이 있는데 computed는 종속된 값에 대한 연산을 수행하기 때문에 이런 문장을 사용하신 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용을 정리해보다 문득 내부 코드가 궁금해져서 &lt;a href=&quot;https://github.com/vuejs/vue&quot;&gt;Vue 레포지토리&lt;/a&gt;에서 관련된 로직을 찾아보고 아래와 같은 결론을 얻을 수 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Computed로직을 먼저 살펴보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;initComputed()&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;1566&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Strsy/btrLb5Bzhds/ek4l0iMahEcDLzmA7L3sjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Strsy/btrLb5Bzhds/ek4l0iMahEcDLzmA7L3sjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Strsy/btrLb5Bzhds/ek4l0iMahEcDLzmA7L3sjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FStrsy%2FbtrLb5Bzhds%2Fek4l0iMahEcDLzmA7L3sjK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;627&quot; height=&quot;674&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;1566&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; initComputed에서는 우리가 컴포넌트 파일 내 computed: 하위에 작성한 메서드들을 반복문을 통해 처리하는 로직인 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 203번째 라인은 다른 data, props, method에 동일한 이름의 메소드, 변수가 있는지 체크해주고, 이상 없을 경우 computed로서 정의해주는 로직으로 이해했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;defindComputed()&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1424&quot; data-origin-height=&quot;1088&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bF8YMe/btrLcJ56NiC/NNV0OxoBkkB9qmC6ry5sp1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bF8YMe/btrLcJ56NiC/NNV0OxoBkkB9qmC6ry5sp1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bF8YMe/btrLcJ56NiC/NNV0OxoBkkB9qmC6ry5sp1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbF8YMe%2FbtrLcJ56NiC%2FNNV0OxoBkkB9qmC6ry5sp1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;581&quot; height=&quot;444&quot; data-origin-width=&quot;1424&quot; data-origin-height=&quot;1088&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; defindComputed 함수에서는 위 initComputed에서 computed로 선언한 메서드들에 대해서 캐시 값을 확인하고 캐시 여부에 따라 createComputedGetter, createGetterInvoker를 통해서 값을 가져오는 것으로 이해했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;method와 관련된 로직을 살펴보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;initMethods()&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1372&quot; data-origin-height=&quot;974&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kPsZO/btrLaWrCDRs/zwautMpfh9QK8mN8ddVGK1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kPsZO/btrLaWrCDRs/zwautMpfh9QK8mN8ddVGK1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kPsZO/btrLaWrCDRs/zwautMpfh9QK8mN8ddVGK1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkPsZO%2FbtrLaWrCDRs%2FzwautMpfh9QK8mN8ddVGK1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;612&quot; height=&quot;434&quot; data-origin-width=&quot;1372&quot; data-origin-height=&quot;974&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; method의 경우 data, props 등 다른 속성과 중복 여부, 함수 여부를 체크하는 로직은 computed와 동일하게 존재했지만, computed로직과는 다르게 cache값을 다루는 로직을 확인할 수는 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부 로직을 보니까 확실하게 이해되었지만, 이게 내가 잘 이해한건지에 대해서는 약간의 찝찝함이 남아있다...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 확실히 computed와 method의 차이는 쉽게 까먹지 않을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;## Reference&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1662061777202&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Computed 속성과 Watch | Vue.js&quot; data-og-description=&quot;Computed 속성과 Watch Computed 속성 템플릿 내에 표현식을 넣으면 편리하지만, 간단한 연산을 위한 부분입니다. 템플릿 안에서 너무 많은 연산을 하면 코드가 비대해지고 유지보수가 어렵습니다. 중&quot; data-og-host=&quot;v3.ko.vuejs.org&quot; data-og-source-url=&quot;https://v3.ko.vuejs.org/guide/computed.html#computed-%E1%84%89%E1%85%A9%E1%86%A8%E1%84%89%E1%85%A5%E1%86%BC%E1%84%8B%E1%85%B4-%E1%84%8F%E1%85%A2%E1%84%89%E1%85%B5%E1%86%BC-vs-%E1%84%86%E1%85%A6%E1%84%89%E1%85%A5%E1%84%83%E1%85%B3&quot; data-og-url=&quot;https://v3.ko.vuejs.org/guide/computed.html#computed-%E1%84%89%E1%85%A9%E1%86%A8%E1%84%89%E1%85%A5%E1%86%BC%E1%84%8B%E1%85%B4-%E1%84%8F%E1%85%A2%E1%84%89%E1%85%B5%E1%86%BC-vs-%E1%84%86%E1%85%A6%E1%84%89%E1%85%A5%E1%84%83%E1%85%B3&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dbZR4M/hyPEOopI6u/6zFgFVHFChNNo79Qq516D1/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400&quot;&gt;&lt;a href=&quot;https://v3.ko.vuejs.org/guide/computed.html#computed-%E1%84%89%E1%85%A9%E1%86%A8%E1%84%89%E1%85%A5%E1%86%BC%E1%84%8B%E1%85%B4-%E1%84%8F%E1%85%A2%E1%84%89%E1%85%B5%E1%86%BC-vs-%E1%84%86%E1%85%A6%E1%84%89%E1%85%A5%E1%84%83%E1%85%B3&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://v3.ko.vuejs.org/guide/computed.html#computed-%E1%84%89%E1%85%A9%E1%86%A8%E1%84%89%E1%85%A5%E1%86%BC%E1%84%8B%E1%85%B4-%E1%84%8F%E1%85%A2%E1%84%89%E1%85%B5%E1%86%BC-vs-%E1%84%86%E1%85%A6%E1%84%89%E1%85%A5%E1%84%83%E1%85%B3&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dbZR4M/hyPEOopI6u/6zFgFVHFChNNo79Qq516D1/img.png?width=400&amp;amp;height=400&amp;amp;face=0_0_400_400');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Computed 속성과 Watch | Vue.js&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Computed 속성과 Watch Computed 속성 템플릿 내에 표현식을 넣으면 편리하지만, 간단한 연산을 위한 부분입니다. 템플릿 안에서 너무 많은 연산을 하면 코드가 비대해지고 유지보수가 어렵습니다. 중&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;v3.ko.vuejs.org&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure id=&quot;og_1662061781430&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;(Vue.js) Computed vs Methods 속성은 어떤 차이점을 가지고 있을까?&quot; data-og-description=&quot; &quot; data-og-host=&quot;sunny921.github.io&quot; data-og-source-url=&quot;https://sunny921.github.io/posts/vuejs-computed-method/&quot; data-og-url=&quot;https://sunny921.github.io/posts/vuejs-computed-method/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/R3Rsd/hyPEKlYtq7/imwJu4gcCs3mCRVsmwWN80/img.png?width=2000&amp;amp;height=1005&amp;amp;face=0_0_2000_1005,https://scrap.kakaocdn.net/dn/TAR9t/hyPENQy1ys/wohlB8m3KKXznkEK8myRS0/img.png?width=1920&amp;amp;height=2560&amp;amp;face=696_1100_1020_1456&quot;&gt;&lt;a href=&quot;https://sunny921.github.io/posts/vuejs-computed-method/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://sunny921.github.io/posts/vuejs-computed-method/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/R3Rsd/hyPEKlYtq7/imwJu4gcCs3mCRVsmwWN80/img.png?width=2000&amp;amp;height=1005&amp;amp;face=0_0_2000_1005,https://scrap.kakaocdn.net/dn/TAR9t/hyPENQy1ys/wohlB8m3KKXznkEK8myRS0/img.png?width=1920&amp;amp;height=2560&amp;amp;face=696_1100_1020_1456');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;(Vue.js) Computed vs Methods 속성은 어떤 차이점을 가지고 있을까?&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;sunny921.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Vue&amp;amp;React</category>
      <category>Vue</category>
      <author>U_pic</author>
      <guid isPermaLink="true">https://u-pic-code.tistory.com/142</guid>
      <comments>https://u-pic-code.tistory.com/142#entry142comment</comments>
      <pubDate>Fri, 2 Sep 2022 04:56:11 +0900</pubDate>
    </item>
    <item>
      <title>[프리온보딩챌린지] 첫번째 리팩토링</title>
      <link>https://u-pic-code.tistory.com/141</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프리온보딩 챌린지 첫번째 강의를 듣고, 지금까지 스스로 했던 부분을 리팩토링 해보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 관심사의 분리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. api 로직의 추상화&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 사용자 피드백 UI 추가&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;1. 관심사의 분리&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드가 하나의 문제, 관심사에 대해서만 충실하게 동작해야 한다. 하나의 코드 안에 여러 가지 동작이 함께 들어있으면 코드가 어떤 기능을 하는지 흐름을 파악하기 힘들고, 코드의 길이도 길어 읽기에도 힘들다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1.1 Route 분리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지는 Route 로직을 따로 분리하지 않고 App.tsx내부에 나열하듯이 코드를 작성하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금은 프로젝트의 크기가 작아서 Route 분기가 적어 크게 문제가 없어 보이지만&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 페이지 수가 많아지면 App.tsx 내부 Route 로직이 길어질 수 있기 때문에&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Route로직을 따로 분리하여 관리해주도록 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고&amp;nbsp; '/todo' 를 공통 경로를 가지고 하위 경로에 따라 다른 화면이 보여지게 되는데 무작정 나열만 해놓은 부분도&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&amp;lt;Before&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1660396906676&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// App.tsx
import React from &quot;react&quot;;
import { Routes, Route } from &quot;react-router-dom&quot;;
import styled from &quot;styled-components&quot;;

import GlobalStyle from &quot;./components/GlobalStyle&quot;;
import Main from &quot;./pages/Main&quot;;
import Auth from &quot;./pages/Auth&quot;;
import Todo from &quot;./pages/Todo&quot;;
import TodoForm from &quot;./components/TodoForm&quot;;
import TodoDetail from &quot;./pages/TodoDetail&quot;;

const ContentWrapper = styled.div`
  position: relative;
  height: 100vh;
`;

function App() {
  return (
    &amp;lt;ContentWrapper&amp;gt;
      &amp;lt;GlobalStyle /&amp;gt;
      &amp;lt;Routes&amp;gt;
        &amp;lt;Route path=&quot;/&quot; element={&amp;lt;Main /&amp;gt;} /&amp;gt;
        &amp;lt;Route path=&quot;/auth&quot; element={&amp;lt;Auth /&amp;gt;} /&amp;gt;
        &amp;lt;Route path=&quot;/todo&quot; element={&amp;lt;Todo /&amp;gt;} /&amp;gt;
        &amp;lt;Route path=&quot;/todo/new&quot; element={&amp;lt;TodoForm /&amp;gt;} /&amp;gt;
        &amp;lt;Route path=&quot;/todo/edit&quot; element={&amp;lt;TodoForm /&amp;gt;} /&amp;gt;
        &amp;lt;Route path=&quot;/todo/:id&quot; element={&amp;lt;TodoDetail /&amp;gt;} /&amp;gt;
      &amp;lt;/Routes&amp;gt;
    &amp;lt;/ContentWrapper&amp;gt;
  );
}

export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;&amp;lt;After&amp;gt;&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1660397478909&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Router/index.tsx

...

function TodoRoute() {
  return (
    &amp;lt;Routes&amp;gt;
      &amp;lt;Route path=&quot;/&quot; element={&amp;lt;Main /&amp;gt;} /&amp;gt;
      &amp;lt;Route path=&quot;/auth&quot; element={&amp;lt;Auth /&amp;gt;} /&amp;gt;
      &amp;lt;Route path=&quot;/todo&quot; element={&amp;lt;Todo /&amp;gt;}&amp;gt;
        &amp;lt;Route path=&quot;new&quot; element={&amp;lt;TodoForm /&amp;gt;} /&amp;gt;
        &amp;lt;Route path=&quot;edit&quot; element={&amp;lt;TodoForm /&amp;gt;} /&amp;gt;
        &amp;lt;Route path=&quot;:id&quot; element={&amp;lt;TodoDetail /&amp;gt;} /&amp;gt;
      &amp;lt;/Route&amp;gt;
    &amp;lt;/Routes&amp;gt;
  );
}

export default TodoRoute;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1660397535499&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// App.tsx
...

import TodoRoute from &quot;./routes&quot;;

...

function App() {
  return (
    &amp;lt;ContentWrapper&amp;gt;
      &amp;lt;GlobalStyle /&amp;gt;
      &amp;lt;TodoRoute /&amp;gt;
    &amp;lt;/ContentWrapper&amp;gt;
  );
}

export default App;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;1.2 로그인 / 회원가입시 폼 검증 로직&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로그인 / 회원가입시 각 input에 들어가는 값을 검증하는 로직을 컴포넌트 내부에 직접 구현했던 것을 모듈로 분리하여 import해서 사용할 수 있게끔 해주었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;530&quot; data-origin-height=&quot;322&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kCNXl/btrJD3zwSvp/wOtQf6QU3c7FmUxCqla1wK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kCNXl/btrJD3zwSvp/wOtQf6QU3c7FmUxCqla1wK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kCNXl/btrJD3zwSvp/wOtQf6QU3c7FmUxCqla1wK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkCNXl%2FbtrJD3zwSvp%2FwOtQf6QU3c7FmUxCqla1wK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;530&quot; height=&quot;322&quot; data-origin-width=&quot;530&quot; data-origin-height=&quot;322&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;2. api 추상화&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 동안 api 로직을 짜면서 별다른 추상화하여 효율적으로 사용해보지 못했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 리팩토링에서 api 로직을 추상화해서 이전보다 효율적인 코드를 작성해보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;axios.create로 apiClient를 만들어 api 요청 공통사항 모듈화&lt;/p&gt;
&lt;pre id=&quot;code_1660412793666&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import axios from &quot;axios&quot;;

export const apiClient = axios.create({
  baseURL: &quot;http://localhost:8080&quot;,
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 auth 요청 관련 로직은 Header가 필요없었지만, todo 관련 로직은 auth 값을 Header에 포함하여 사용해야한다. 여기서 client를 todo와 auth 두 개를 만들어야할까도 잠시 고민했었지만, 동일한 역할을 하는 client가 두 개가 생기는 것이라고 생각해서, todo에 필요한 auth Header 값은 각 요청에서 전달하는 방향으로 리팩토링하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 만든 Client에서 baseURL이 선언되어 있기 때문에 기존에 URL 전체를 통해 요청하던 코드를 아래와 같이 수정했다.&lt;/p&gt;
&lt;pre id=&quot;code_1660413015365&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;const login = async (email: string, password: string) =&amp;gt; {
  try {
    const response = await apiClient.post&amp;lt;Data&amp;gt;(&quot;/users/login&quot;, {
      email,
      password,
    });

  ...
};&lt;/code&gt;&lt;/pre&gt;</description>
      <author>U_pic</author>
      <guid isPermaLink="true">https://u-pic-code.tistory.com/141</guid>
      <comments>https://u-pic-code.tistory.com/141#entry141comment</comments>
      <pubDate>Mon, 15 Aug 2022 04:26:40 +0900</pubDate>
    </item>
    <item>
      <title>[React] React Router의 state 속성 사용시 타입 이슈</title>
      <link>https://u-pic-code.tistory.com/140</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;문제 상황&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;react-router-dom 패키지를 사용하면서 Link 태그를 통해 다른 url로 이동하는 버튼을 만들어 주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 Link 태그의 state 속성을 사용해 이동하는 페이지에 특정 데이터를 전달하는 코드를 작성하였다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;130&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RSUO6/btrI1hSCNxy/BBkbtyfSJ3lMajTyZmksY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RSUO6/btrI1hSCNxy/BBkbtyfSJ3lMajTyZmksY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RSUO6/btrI1hSCNxy/BBkbtyfSJ3lMajTyZmksY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRSUO6%2FbtrI1hSCNxy%2FBBkbtyfSJ3lMajTyZmksY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1456&quot; height=&quot;130&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;130&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이동한 페이지에서 Location과 관련한 값을 useLocation() Hook을 활용하여 받아오도록 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 데이터는 pathname과 state다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 문제가 발생했는데 기존에 타입을 지정하는 방식대로 pathname과 state를 지정해주었을 때 타입과 관련한 에러가 발생했다.&lt;/p&gt;
&lt;pre id=&quot;code_1659716368716&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;interface {
	pathname: string;
    state: {
    	id: string;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-08-05 오전 2.42.47.png&quot; data-origin-width=&quot;1436&quot; data-origin-height=&quot;524&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c6vX34/btrI2xUIPNv/2j8hGyfAgjz7cjidTu2Xs1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c6vX34/btrI2xUIPNv/2j8hGyfAgjz7cjidTu2Xs1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c6vX34/btrI2xUIPNv/2j8hGyfAgjz7cjidTu2Xs1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc6vX34%2FbtrI2xUIPNv%2F2j8hGyfAgjz7cjidTu2Xs1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1436&quot; height=&quot;524&quot; data-filename=&quot;스크린샷 2022-08-05 오전 2.42.47.png&quot; data-origin-width=&quot;1436&quot; data-origin-height=&quot;524&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러가지 구글링을 해본 결과 아래의 사진과 같이 타입을 Generic 하게 선언해주는 방법도 사용해보았지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에러 메세지를 보았을 때, 이 방식으로&amp;nbsp; 타입을 선언하는 방식은 존재하지 않는 것 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-08-05 오전 2.43.01.png&quot; data-origin-width=&quot;1606&quot; data-origin-height=&quot;424&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bRXOHk/btrIYnMP9Cu/WrsnfFokfa12eBgzkOzNu0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bRXOHk/btrIYnMP9Cu/WrsnfFokfa12eBgzkOzNu0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bRXOHk/btrIYnMP9Cu/WrsnfFokfa12eBgzkOzNu0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbRXOHk%2FbtrIYnMP9Cu%2FWrsnfFokfa12eBgzkOzNu0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1606&quot; height=&quot;424&quot; data-filename=&quot;스크린샷 2022-08-05 오전 2.43.01.png&quot; data-origin-width=&quot;1606&quot; data-origin-height=&quot;424&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React Router v6 공식문서를 보았을 때 state의 타입 자체가 state의 타입 자체가 unknown으로 정의되어 있어서 내가 정의한 타입이 적용되지 않았고, Location 타입이 Generic 하게 만들어지지 않았기 때문에 다른 블로그에서 본 내용도 적용할 수 없었던 것 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2106&quot; data-origin-height=&quot;1496&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4RglL/btrIXAZAZMl/z7qXIkp1UDAYB2oqnebil1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4RglL/btrIXAZAZMl/z7qXIkp1UDAYB2oqnebil1/img.png&quot; data-alt=&quot;https://reactrouter.com/docs/en/v6/hooks/use-location&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4RglL/btrIXAZAZMl/z7qXIkp1UDAYB2oqnebil1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4RglL%2FbtrIXAZAZMl%2Fz7qXIkp1UDAYB2oqnebil1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2106&quot; height=&quot;1496&quot; data-origin-width=&quot;2106&quot; data-origin-height=&quot;1496&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://reactrouter.com/docs/en/v6/hooks/use-location&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;location을 console로 찍어봤을 땐 원하는 값이 잘 들어있는데...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왜 타입을 Generic이 아닌 그냥 unknown으로 해두었을까...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Link의 Props를 통해 값을 전달하는 것을 막고 싶었으면 저 state자체를 없앴을 텐데...&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1242&quot; data-origin-height=&quot;292&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIDiA3/btrIY0w78Gx/shhR4hoNHe98dD53lXAfL0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIDiA3/btrIY0w78Gx/shhR4hoNHe98dD53lXAfL0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIDiA3/btrIY0w78Gx/shhR4hoNHe98dD53lXAfL0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIDiA3%2FbtrIY0w78Gx%2FshhR4hoNHe98dD53lXAfL0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1242&quot; height=&quot;292&quot; data-origin-width=&quot;1242&quot; data-origin-height=&quot;292&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 아래 코드와 같이 as를 통해 타입을 단언해주었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1184&quot; data-origin-height=&quot;82&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rysP5/btrI4VHlQac/ekXwIPME67pk8IXKWznIDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rysP5/btrI4VHlQac/ekXwIPME67pk8IXKWznIDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rysP5/btrI4VHlQac/ekXwIPME67pk8IXKWznIDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrysP5%2FbtrI4VHlQac%2FekXwIPME67pk8IXKWznIDk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1184&quot; height=&quot;82&quot; data-origin-width=&quot;1184&quot; data-origin-height=&quot;82&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;s&gt;내가 사용한 코드에서는 state의 값이 as로 타입을 단언하는 방식이 아닌 것 같지만&lt;/s&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;s&gt;내가 작성한 코드의 state는 id말고는 다른 데이터를 사용하지 않기 때문에 타입을 단언해서 사용하는 것도 하나의 방법이 될 수 있겠다는 생각을 했다.&lt;/s&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <category>Vue&amp;amp;React</category>
      <category>reactrouter</category>
      <category>TypeScript</category>
      <author>U_pic</author>
      <guid isPermaLink="true">https://u-pic-code.tistory.com/140</guid>
      <comments>https://u-pic-code.tistory.com/140#entry140comment</comments>
      <pubDate>Sat, 6 Aug 2022 03:01:52 +0900</pubDate>
    </item>
    <item>
      <title>[Vue.js] Sass 사용시 변수를 전역에서 사용하기</title>
      <link>https://u-pic-code.tistory.com/139</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Sass를 사용하면 원하는 디자인 값을 변수에 할당하여 사용할 수 있는 장점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디자인 변수를 사용하면 나중에 변수의 값만 바꾸어 줌으로써 유지보수 부분에서 엄청난 장점을 가지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 진행하고 있는 프로젝트의 사이즈가 조금 더 커질 예정이라 Sass 변수를 사용해보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용할 변수는&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;270&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/pHzcL/btrH7558jqD/upmayWwGQweSaX4MgUR9Y0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/pHzcL/btrH7558jqD/upmayWwGQweSaX4MgUR9Y0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/pHzcL/btrH7558jqD/upmayWwGQweSaX4MgUR9Y0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FpHzcL%2FbtrH7558jqD%2FupmayWwGQweSaX4MgUR9Y0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;511&quot; height=&quot;270&quot; data-origin-width=&quot;700&quot; data-origin-height=&quot;270&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 많이 사용할 것 같은 프로젝트 메인 컬러와 서브 컬러로 우선 테스트해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Vue 프로젝트에서 변수명을 사용할 경우,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변수 파일을 만들고, 해당 각 파일을 import 해서 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1658717627190&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Main.vue

...

&amp;lt;style lang='sass'&amp;gt;
@import '@/assets/sass/common.sass'

.main {
	background: $MAIN-COLOR;
}
&amp;lt;/style&amp;gt;

...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 여러 변수를 사용하다 보면 대부분의 파일에서 변수 파일을 import 할 텐데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 파일에 `@import ~`를 써주는 코드 중복도 해결해 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존의 vue.config.js 파일에 아래 코드를 추가해주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일이 없다면 root 경로에 vue.config.js 파일을 추가하고 작성한다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot;&gt;&lt;code&gt;module.exports = {
  css : {
    loaderOptions : {
      sass : {
        additionalData: `
          @import &quot;@/assets/sass/common.scss&quot;;
        `
      }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;### Reference&lt;/b&gt;&lt;/h4&gt;
&lt;figure id=&quot;og_1658718249408&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Vue. vue-cli에서 SCSS 적용과 Global SCSS 적용&quot; data-og-description=&quot;Vue-Cli에서 SCSS 적용 Vue에서는 덕분에 Single File Component 구성의 .vue 파일 내에서 태그에 요소를 추가하여 사용할 수 있다. SCSS는 사용함에 있어 또는 , 를 사전에 별도의 파일로 분리하여 사용하기&quot; data-og-host=&quot;yilpe93.github.io&quot; data-og-source-url=&quot;https://yilpe93.github.io/vue/vue-set-scss/&quot; data-og-url=&quot;https://yilpe93.github.io/vue/vue-set-scss/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://yilpe93.github.io/vue/vue-set-scss/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://yilpe93.github.io/vue/vue-set-scss/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Vue. vue-cli에서 SCSS 적용과 Global SCSS 적용&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Vue-Cli에서 SCSS 적용 Vue에서는 덕분에 Single File Component 구성의 .vue 파일 내에서 태그에 요소를 추가하여 사용할 수 있다. SCSS는 사용함에 있어 또는 , 를 사전에 별도의 파일로 분리하여 사용하기&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;yilpe93.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Vue&amp;amp;React</category>
      <category>Sass</category>
      <category>Vue</category>
      <author>U_pic</author>
      <guid isPermaLink="true">https://u-pic-code.tistory.com/139</guid>
      <comments>https://u-pic-code.tistory.com/139#entry139comment</comments>
      <pubDate>Mon, 25 Jul 2022 12:05:06 +0900</pubDate>
    </item>
    <item>
      <title>What's in my package.json?</title>
      <link>https://u-pic-code.tistory.com/138</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트들을 시작하면서 CRA나 Vue cli로 간편하게 프로젝트들을 생성해서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;package.json의 dependencies, devdependencies를 제외하고 어떤 내용들이 포함되어있는지 제대로 본 적이 없었던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 내가 최근까지 했던 saai-peer프로젝트의 package.json과&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;github에서 vue 레포 지토리중 star 수가 가장 많았던 vue.js의 package.json을 열어보고 안에 어떤 내용이 있는지 보려고 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3046&quot; data-origin-height=&quot;1258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XgDFd/btrHp1Xvfyd/pMrWaUPd7ifylo9JPBhGk0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XgDFd/btrHp1Xvfyd/pMrWaUPd7ifylo9JPBhGk0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XgDFd/btrHp1Xvfyd/pMrWaUPd7ifylo9JPBhGk0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXgDFd%2FbtrHp1Xvfyd%2FpMrWaUPd7ifylo9JPBhGk0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3046&quot; height=&quot;1258&quot; data-origin-width=&quot;3046&quot; data-origin-height=&quot;1258&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 내 프로젝트의 package.json을 먼저 보면 아래와 같은 내용들이 들어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;&lt;s&gt;package.json에 lint, prettier를 제외하고 전혀 세팅을 안 해봐서 별로 내용이 없긴 하다...&lt;/s&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;edited_Package-1.jpg&quot; data-origin-width=&quot;1668&quot; data-origin-height=&quot;1565&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bi2L92/btrHp2oyLdJ/cCJORmFKfb77p7kbrlO5t1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bi2L92/btrHp2oyLdJ/cCJORmFKfb77p7kbrlO5t1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bi2L92/btrHp2oyLdJ/cCJORmFKfb77p7kbrlO5t1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbi2L92%2FbtrHp2oyLdJ%2FcCJORmFKfb77p7kbrlO5t1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;710&quot; height=&quot;666&quot; data-filename=&quot;edited_Package-1.jpg&quot; data-origin-width=&quot;1668&quot; data-origin-height=&quot;1565&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;name&lt;/b&gt; : 프로젝트 이름&lt;/li&gt;
&lt;li&gt;&lt;b&gt;version&lt;/b&gt;: 프로젝트 버전&lt;/li&gt;
&lt;li&gt;&lt;b&gt;scripts&lt;/b&gt;: 프로젝트 명령어, 항목 별로 키는 이벤트이고, 값은 이벤트를 발생했을 때 동작할 커멘드이다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;description&lt;/b&gt;: 프로젝트 설명&lt;/li&gt;
&lt;li&gt;&lt;b&gt;author&lt;/b&gt;: 프로젝트 작성자&lt;/li&gt;
&lt;li&gt;&lt;b&gt;dependencies&lt;/b&gt;: production 환경에서 응용프로그램에 필요한 의존성을 담고 있는 부분&lt;/li&gt;
&lt;li&gt;&lt;b&gt;devdependencies&lt;/b&gt;: 개발 단계에서 필요한 의존성 모듈을 담고 있는 부분. 테스트 / lint&amp;amp;prettier와 같은 의존성들이 들어있다.&lt;br /&gt;devdependencies에 설치하기 위해서는 `npm install [ 라이브러리명 ] --save -dev`로 설치한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 vue.js의 package.json을 보았다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Package-2.jpg&quot; data-origin-width=&quot;1668&quot; data-origin-height=&quot;2154&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/H6JS8/btrHqnspiiQ/CpWA1G3hG7f07wsebbrP8K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/H6JS8/btrHqnspiiQ/CpWA1G3hG7f07wsebbrP8K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/H6JS8/btrHqnspiiQ/CpWA1G3hG7f07wsebbrP8K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FH6JS8%2FbtrHqnspiiQ%2FCpWA1G3hG7f07wsebbrP8K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;604&quot; height=&quot;780&quot; data-filename=&quot;Package-2.jpg&quot; data-origin-width=&quot;1668&quot; data-origin-height=&quot;2154&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;exports: 외부에서 요청하여 사용할 모듈을 선언하는 부분, exports에 작성하면 exports에 포함된 모듈만 사용 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;exports 필드는 객체를 가지며 각 프로퍼티에 모듈 요청의 하위 경로가 포함된다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sideEffects: Tree Shaking에 사용, sideEffects를 false로 해서 사용하지 않는 exports는 제거해도 된다는 것을 webpack에 알린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;githooks: git 특정 시점에서 특정 작업을 trigger할 수 있다. 보통 lint 검사 / test 코드 실행을 githooks로 자동화한다. githooks를 만들어두면 적절한 시점마다 반복되는 작업을 자동화할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;lint-staged: 대상 파일을 지정하고 조건에 맞는 파일이 커밋되었을 때 세팅한 명령어를 적용시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Package-3.jpg&quot; data-origin-width=&quot;1668&quot; data-origin-height=&quot;2154&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqdDlU/btrHrNYu35C/8sDqttQDL8Oyea1sIFQM9K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqdDlU/btrHrNYu35C/8sDqttQDL8Oyea1sIFQM9K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqdDlU/btrHrNYu35C/8sDqttQDL8Oyea1sIFQM9K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqdDlU%2FbtrHrNYu35C%2F8sDqttQDL8Oyea1sIFQM9K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;656&quot; height=&quot;847&quot; data-filename=&quot;Package-3.jpg&quot; data-origin-width=&quot;1668&quot; data-origin-height=&quot;2154&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;repository: 소스코드가 관리되는 저장소의 위치&lt;/li&gt;
&lt;li&gt;keywords: 문자열 배열을 값으로 가지며, 현재 패키지를 찾을 수 있도록 도와준다.&lt;br /&gt;`npm search [ 검색어 ]`를 통해 콘솔 / 터미널에서 패키지 검색이 가능하다.&lt;img src=&quot;https://blog.kakaocdn.net/dn/Cg5Mc/btrHqCQqVTr/GZo0vd6Wq5Ef1aABAmTGL1/img.png&quot; data-origin-width=&quot;2030&quot; data-origin-height=&quot;922&quot; data-is-animation=&quot;false&quot; /&gt;&lt;/li&gt;
&lt;li&gt;bugs: 프로젝트의 이슈 / 버그 트래킹을 볼 수 있는 url 또는 이슈 버그를 알릴 수 있는 이메일을 넣어둘 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;### Reference&lt;/b&gt;&lt;/h4&gt;
&lt;figure id=&quot;og_1657906380219&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Package exports | 웹팩&quot; data-og-description=&quot;웹팩은 모듈 번들러입니다. 주요 목적은 브라우저에서 사용할 수 있도록 JavaScript 파일을 번들로 묶는 것이지만, 리소스나 애셋을 변환하고 번들링 또는 패키징할 수도 있습니다.&quot; data-og-host=&quot;webpack.kr&quot; data-og-source-url=&quot;https://webpack.kr/guides/package-exports/&quot; data-og-url=&quot;https://webpack.kr/guides/package-exports/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/DIu3S/hyO5QHlZe8/ubkB8PRhW5KA7qkvSNsemk/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512&quot;&gt;&lt;a href=&quot;https://webpack.kr/guides/package-exports/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://webpack.kr/guides/package-exports/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/DIu3S/hyO5QHlZe8/ubkB8PRhW5KA7qkvSNsemk/img.png?width=512&amp;amp;height=512&amp;amp;face=0_0_512_512');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Package exports | 웹팩&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;웹팩은 모듈 번들러입니다. 주요 목적은 브라우저에서 사용할 수 있도록 JavaScript 파일을 번들로 묶는 것이지만, 리소스나 애셋을 변환하고 번들링 또는 패키징할 수도 있습니다.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;webpack.kr&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;figure id=&quot;og_1657906390003&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;package.json | npm Docs&quot; data-og-description=&quot;Specifics of npm's package.json handling&quot; data-og-host=&quot;docs.npmjs.com&quot; data-og-source-url=&quot;https://docs.npmjs.com/cli/v8/configuring-npm/package-json&quot; data-og-url=&quot;https://docs.npmjs.com/cli/v8/configuring-npm/package-json/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cebQSO/hyO5NqkfrA/HK3ubBRQYbHm35DH3g9Uf0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://docs.npmjs.com/cli/v8/configuring-npm/package-json&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.npmjs.com/cli/v8/configuring-npm/package-json&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cebQSO/hyO5NqkfrA/HK3ubBRQYbHm35DH3g9Uf0/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;package.json | npm Docs&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Specifics of npm's package.json handling&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.npmjs.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>지나가는 개념 정리</category>
      <category>package.json</category>
      <author>U_pic</author>
      <guid isPermaLink="true">https://u-pic-code.tistory.com/138</guid>
      <comments>https://u-pic-code.tistory.com/138#entry138comment</comments>
      <pubDate>Sat, 16 Jul 2022 02:34:17 +0900</pubDate>
    </item>
  </channel>
</rss>