<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>jun's.log</title>
        <link>https://velog.io/</link>
        <description>HiHi</description>
        <lastBuildDate>Wed, 06 Mar 2024 12:05:06 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. jun's.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/jun_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[플러터] 11. 디지털 주사위]]></title>
            <link>https://velog.io/@jun_/%ED%94%8C%EB%9F%AC%ED%84%B0-11.-%EB%94%94%EC%A7%80%ED%84%B8-%EC%A3%BC%EC%82%AC%EC%9C%84</link>
            <guid>https://velog.io/@jun_/%ED%94%8C%EB%9F%AC%ED%84%B0-11.-%EB%94%94%EC%A7%80%ED%84%B8-%EC%A3%BC%EC%82%AC%EC%9C%84</guid>
            <pubDate>Wed, 06 Mar 2024 12:05:06 GMT</pubDate>
            <description><![CDATA[<h2 id="사전-지식">사전 지식</h2>
<h3 id="shake-플러그인">shake 플러그인</h3>
<p>shake 플러그인을 사용하여 모바일 기기 흔들림을 감지할 수 있습니다.</p>
<h3 id="tabbarview-tabcontroller">TabBarView, TabController</h3>
<p>TabBarView 와 TabController를 이용하여 여러 스크린을 탭으로 나누고 탭 클릭 시 스크린 전환이 가능합니다.</p>
<h2 id="탭-화면-전환-코드">탭 화면 전환 코드</h2>
<pre><code class="language-dart">import &#39;package:flutter/material.dart&#39;;
import &#39;package:ch11/screen/home_screen.dart&#39;;
import &#39;package:ch11/screen/setting_screen.dart&#39;;

class RootScreen extends StatefulWidget {
  const RootScreen({Key? key}) : super(key: key);

  @override
  _RootScreenState createState() =&gt; _RootScreenState();
}

// 탭 컨트롤러를 초기화하려면 vsync가 필요하므로 TickerProviderStateMixin을 추가
// TickerProviderStateMixin은 애니메이션의 효율을 올리주는 역할을 함 (fps와 렌더링 주기를 맞춰줌)
class _RootScreenState extends State&lt;RootScreen&gt; with TickerProviderStateMixin {
  TabController? tabController;

  void tabListener() {}

  @override
  void initState() {
    super.initState();

    // 탭 컨트롤러 초기화 (탭의 개수와 TickerProviderMixin을 사용하는 State클래스를 this 형태로 주입)
    tabController = TabController(length: 2, vsync: this);

    // 탭 컨트롤러 속성이 변경될 때마다 실행할 함수 등록
    tabController!.addListener(tabListener);
  }

  // 위젯 삭제 시 실행되는 함수
  @override
  void dispose() {
    tabController!.removeListener(tabListener);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: TabBarView(
        controller: tabController,
        children: [
          HomeScreen(),
          SettingScreen(),
        ],
      ),
      bottomNavigationBar: BottomNavigationBar(
        // 현재 화면에 렌더링되는 탭의 인덱스
        currentIndex: tabController!.index,

        // 탭이 선택될 때마다 실행되는 함수
        onTap: (int index) {
          setState(() {
            // 선택한 탭의 인덱스로 애니메이션을 적용하여 이동함
            tabController!.animateTo(index);
          });
        },
        items: [
          BottomNavigationBarItem(
            icon: Icon(
              Icons.edgesensor_high_outlined,
            ),
            label: &#39;주사위&#39;,
          ),
          BottomNavigationBarItem(
            icon: Icon(
              Icons.settings,
            ),
            label: &#39;설정&#39;,
          ),
        ],
      ),
    );
  }
}</code></pre>
<blockquote>
<p>《Must Have 코드팩토리의 플러터 프로그래밍 2판》의 스터디 내용 입니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[플러터] 10. 만난 지 며칠 U&I]]></title>
            <link>https://velog.io/@jun_/%ED%94%8C%EB%9F%AC%ED%84%B0-10.-%EB%A7%8C%EB%82%9C-%EC%A7%80-%EB%A9%B0%EC%B9%A0-UI</link>
            <guid>https://velog.io/@jun_/%ED%94%8C%EB%9F%AC%ED%84%B0-10.-%EB%A7%8C%EB%82%9C-%EC%A7%80-%EB%A9%B0%EC%B9%A0-UI</guid>
            <pubDate>Wed, 06 Mar 2024 10:50:38 GMT</pubDate>
            <description><![CDATA[<h2 id="사전-지식">사전 지식</h2>
<h3 id="setstate-함수">setState() 함수</h3>
<p>State를 상속하는 모든 클래스는 setState() 함수를 사용할 수 있습니다. setState() 함수가 실행되는 과정은 다음과 같이 5단계입니다.</p>
<ol>
<li>클린 상태</li>
<li>setState()</li>
<li>더티 상태</li>
<li>build()</li>
<li>클린 상태</li>
</ol>
<p>setState() 함수는 매개변수로 콜백함수를 입력받습니다.
단, 비동기 함수는 안됩니다.</p>
<h3 id="showcupertinodialog-함수">showCupertinoDialog() 함수</h3>
<p>ios 스타일로 다이얼로그가 실행되는 함수입니다.</p>
<pre><code class="language-dart">import &#39;package:flutter/cupertino.dart

showCupertinoDialog(
  context: context, // BuildContext 입력
  barrierDismissible: true, // 외부 탭해서 닫을 수 있음
  builder: (BuildContext context) { // 다이얼로그에 들어갈 위젯
    return Text(&#39;Dialog&#39;);
  },
);</code></pre>
<h2 id="코드">코드</h2>
<pre><code class="language-dart">import &#39;package:flutter/cupertino.dart&#39;;
import &#39;package:flutter/material.dart&#39;;

class HomeScreen extends StatefulWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  State&lt;HomeScreen&gt; createState() =&gt; _HomeScreenState();
}

class _HomeScreenState extends State&lt;HomeScreen&gt; {
  DateTime firstDay = DateTime.now();

  void onHeartPressed() {
    showCupertinoDialog( // 다이얼로그 실행
      context: context,
      builder: (BuildContext context) {
        return Align( // child를 정렬함
          alignment: Alignment.bottomCenter,
          child: Container(
            height: 300,
            child: CupertinoDatePicker( // 날짜 선택 위젯
              mode: CupertinoDatePickerMode.date,
              onDateTimeChanged: (DateTime date) {
                setState(() { // 날짜 변경 시 firstDay를 변경하로 리빌드
                  firstDay = date;
                });
              },
            ),
          ),
        );
      },
      barrierDismissible: true, // 외부 탭으로 위젯 닫음
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.pink[100],
      body: SafeArea(
        top: true,
        bottom: false,
        child: Column( // children을 세로로 정렬
          mainAxisAlignment: MainAxisAlignment.spaceBetween, // 위, 아래 양 끝으로 정렬
          crossAxisAlignment: CrossAxisAlignment.stretch, // 가로의 너비를 최대로
          children: [
            _DDay( // 날짜를 표시하는 위젯으로 상태 값과 이벤트 핸들러를 주입
              onHeartPressed: onHeartPressed,
              firstDay: firstDay,
            ),
            _CoupleImage(),
          ],
        ),
      ),
    );
  }
}

class _DDay extends StatelessWidget {
  final GestureTapCallback onHeartPressed;
  final DateTime firstDay;

  _DDay({
    required this.onHeartPressed,
    required this.firstDay,
  });

  @override
  Widget build(BuildContext context) {
    final now = DateTime.now();

    return Column(
      children: [
        const SizedBox(height: 16.0),
        Text(
          &#39;U&amp;I&#39;,
        ),
        const SizedBox(height: 16.0),
        Text(
          &#39;우리 처음 만난 날&#39;,
        ),
        const SizedBox(height: 16.0),
        Text(
          &#39;2023.08.08&#39;,
        ),
        const SizedBox(height: 16.0),
        IconButton(
          iconSize: 60.0,
          onPressed: this.onHeartPressed,
          icon: Icon(
            Icons.favorite,
          ),
        ),
        const SizedBox(height: 16.0),
        Text(
            &#39;D+${DateTime(now.year, now.month, now.day).difference(firstDay).inDays + 1}&#39;)
      ],
    );
  }
}

class _CoupleImage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Expanded(
      child: Center(
        child: Image.asset(
          &#39;asset/img/middle_image.png&#39;,
          height: MediaQuery.of(context).size.height / 2,
        ),
      ),
    );
  }
}</code></pre>
<blockquote>
<p>《Must Have 코드팩토리의 플러터 프로그래밍 2판》의 스터디 내용 입니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[플러터] 9. 전자액자: 위젯 생명주기]]></title>
            <link>https://velog.io/@jun_/%ED%94%8C%EB%9F%AC%ED%84%B0-9.-%EC%A0%84%EC%9E%90%EC%95%A1%EC%9E%90-%EC%9C%84%EC%A0%AF-%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0</link>
            <guid>https://velog.io/@jun_/%ED%94%8C%EB%9F%AC%ED%84%B0-9.-%EC%A0%84%EC%9E%90%EC%95%A1%EC%9E%90-%EC%9C%84%EC%A0%AF-%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0</guid>
            <pubDate>Wed, 28 Feb 2024 11:23:16 GMT</pubDate>
            <description><![CDATA[<h2 id="위젯-생명주기">위젯 생명주기</h2>
<h3 id="statelesswidget">StatelessWidget</h3>
<p>상태가 없는 위젯입니다.
생명주기는 먼저 해당 위젯이 빌드되면 생성자가 실행됩니다.
이어서 필수로 오버라이드 해야 하는 build() 함수가 실행됩니다.
마지막으로 build() 함수에 반환한 위젯이 화면에 렌더링됩니다.
StatelessWidget의 build() 함수는 재실행되지 않습니다.</p>
<h3 id="statefulwidget">StatefulWidget</h3>
<p>build()가 실행되기까지 과정은 StatelessWidget과 같습니다.
반면에 StatefulWidget은 build()를 재실행할 수 있습니다.</p>
<h4 id="기본-생명주기">기본 생명주기</h4>
<p>상태변경이 없는 생명주기는 나타나고 사라지기까지 중간에 위젯의 상태가 변경되지 않습니다</p>
<ol>
<li>StatefulWidget 생성자가 실행됩니다.</li>
<li>createState() 함수가 실행되며, StatefulWidget과 연동되는 State를 생성합니다.</li>
<li>State가 생성되면 initState()가 실행됩니다. (최초 1번)</li>
<li>didChangeDependencies()가 실행됩니다. (State가 의존하는 값이 변경되면 반복)</li>
<li>State의 상태가 dirty로 설정됩니다.</li>
<li>dirty로 인해 build()가 재실행되고, 화면에 반영됩니다.</li>
<li>build가 완료되면 clean상태가 됩니다.</li>
<li>위젯이 위젯트리에서 사라지면 deactivate()가 실행됩니다. (State가 일시적 또는 영구적으로 삭제될 때)</li>
<li>위젯이 영구적으로 삭제될 때 dispose()가 실행됩니다.</li>
</ol>
<h4 id="생성자의-매개변수가-변경되었을-때-생명주기">생성자의 매개변수가 변경되었을 때 생명주기</h4>
<ol>
<li>StatefulWidget 생성자가 실행됩니다.</li>
<li>State의 didUpdateWidget() 함수가 실행됩니다.</li>
<li>State가 dirty 상태로 변경됩니다.</li>
<li>build()가 실행됩니다.</li>
<li>State의 상태가 clean으로 변경됩니다.</li>
</ol>
<h4 id="state-자체적으로-build를-재실행할-때-생명주기">State 자체적으로 build()를 재실행할 때 생명주기</h4>
<ol>
<li>State 클래스의 setState()를 실행합니다.</li>
<li>State가 dirty 상태로 변경됩니다.</li>
<li>build()가 실행됩니다.</li>
<li>State의 상태가 clean으로 변경됩니다.</li>
</ol>
<h2 id="전자액자-코드">전자액자 코드</h2>
<h4 id="maindart">main.dart</h4>
<pre><code class="language-dart">import &#39;package:ch09/screen/home_screen.dart&#39;;
import &#39;package:flutter/material.dart&#39;;

void main() {
  runApp(
    MaterialApp(
      home: HomeScreen(),
    ),
  );
}</code></pre>
<h4 id="screenhome_screendart">/screen/home_screen.dart</h4>
<pre><code class="language-dart">import &#39;package:flutter/material.dart&#39;;
import &#39;dart:async&#39;; // 타이머를 사용하기 위해 import

// 타이머를 사용하기 위해 StatefulWidget 사용
class HomeScreen extends StatefulWidget {
  const HomeScreen({Key? key}) : super(key: key);

  // 연동할 State를 생성
  @override
  State&lt;HomeScreen&gt; createState() =&gt; _HomeScreenState();
}

// 상태는 StatefulWidget을 매개변수로 받는 State 클래스를 상속 받음 (타입이 State&lt;HomeScreen&gt; 이 됨)
class _HomeScreenState extends State&lt;HomeScreen&gt; {
  final PageController pageController = PageController();

  // State가 생성되면 initState() 실행됨
  @override
  void initState() {
    super.initState();

    // initState()는 최초 1번만 실행되기 때문에 여기서 Timer를 생성함
    Timer.periodic(Duration(seconds: 3), (timer) {
      int? nextPage = pageController.page?.toInt();

      if (nextPage == null) {
        return;
      }

      if (nextPage == 4) {
        nextPage = 0;
      } else {
        nextPage += 1;
      }

      // 타이머에 설정한 시간 간격으로 페이지를 이동시킴
      pageController.animateToPage(
        nextPage,
        duration: Duration(milliseconds: 500),
        curve: Curves.ease,
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: PageView(
        controller: pageController,
        // 5개의 이미지를 페이지로 나열
        children: [1, 2, 3, 4, 5]
            .map((number) =&gt; Image.asset(
                  &#39;asset/img/image_$number.jpeg&#39;,
                  fit: BoxFit.cover,
                ))
            .toList(),
      ),
    );
  }
}
</code></pre>
<blockquote>
<p>《Must Have 코드팩토리의 플러터 프로그래밍 2판》의 스터디 내용 입니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[플러터] 8. 블로그 웹앱: 콜백 함수, 웹뷰, 네이티브 설정]]></title>
            <link>https://velog.io/@jun_/%ED%94%8C%EB%9F%AC%ED%84%B0-8.-%EB%B8%94%EB%A1%9C%EA%B7%B8-%EC%9B%B9%EC%95%B1-%EC%BD%9C%EB%B0%B1-%ED%95%A8%EC%88%98-%EC%9B%B9%EB%B7%B0-%EB%84%A4%EC%9D%B4%ED%8B%B0%EB%B8%8C-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@jun_/%ED%94%8C%EB%9F%AC%ED%84%B0-8.-%EB%B8%94%EB%A1%9C%EA%B7%B8-%EC%9B%B9%EC%95%B1-%EC%BD%9C%EB%B0%B1-%ED%95%A8%EC%88%98-%EC%9B%B9%EB%B7%B0-%EB%84%A4%EC%9D%B4%ED%8B%B0%EB%B8%8C-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Tue, 27 Feb 2024 16:11:47 GMT</pubDate>
            <description><![CDATA[<h2 id="사전-지식">사전 지식</h2>
<h3 id="콜백함수">콜백함수</h3>
<p> 콜백함수는 일정 작업이 완료되면 실행되는 함수입니다.</p>
<pre><code class="language-dart"> WebviewController controller = WebviewController();
   ..setNavigationDelegate(NavigationDelegate(
     onPageFinished: (String url) {
       print(url);
     }
   ))</code></pre>
<p> onPageFinished()는 웹뷰에서 페이지 로딩이 완료된 뒤에 실행되는 콜백함수 입니다.
 이외에도 onWebviewCreated(), onPageStarted(), onProgress() 등 특정 조건이 성립되었을 때 실행되는 함수도 있습니다.</p>
<h3 id="웹뷰-위젯">웹뷰 위젯</h3>
<p> 웹뷰는 프레임워크에 내장된 브라우저를 앱의 네이티브 컴포넌트에 임베딩하는 기능입니다.
 즉 앱에서 웹 브라우저 기능을 구현해주는 기술입니다.</p>
<h2 id="사전-준비">사전 준비</h2>
<h3 id="pubspecyaml">pubspec.yaml</h3>
<p> 웹뷰를 사용하기 위해 관련 플러그인을 설치합니다.</p>
<pre><code class="language-dart"> dependencies:
  flutter:
    sdk: flutter

  webview_flutter: 4.4.1</code></pre>
<pre><code> flutter pub get</code></pre><h3 id="권한-및-네이티브-설정하기">권한 및 네이티브 설정하기</h3>
<p> 웹뷰를 사용하려면 몇 가지 네이티브 설정이 필요합니다.</p>
<ul>
<li><p>인터넷 사용 권한</p>
</li>
<li><p>버전 설정</p>
</li>
<li><p>(선택) http 설정</p>
<h4 id="안드로이드">안드로이드</h4>
<p>android/app/src/main/AndroidManifest.xml에서 아래와 같이 인터넷 권한을 추가합니다.</p>
<pre><code class="language-xml">&lt;manifest xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
 package=&quot;com.example.blog_web_app&quot;&gt;
 &lt;uses-permission android:name=&quot;android.permission.INTERNET&quot; /&gt;
 ...
&lt;/manifest&gt;
</code></pre>
<p>다음으로 웹뷰를 사용하기 위해 버전을 변경해줍니다.
android/app/build.gradle 에서 아래와 같이 변경합니다.
android/app/build.gradle은 모듈 파일이며 의존성이나 버전 정보를 관리합니다.
참고로 android/build.gradle과 andgroid/app/build.gradle은 서로 다른 파일이니 유의해줍니다.</p>
<pre><code class="language-gradle">android {
 compileSdkVersion 33
 ..

 defaultConfig {
     applicationId &quot;com.example.blog_web_app&quot;
     minSdkVersion 20 // webview_flutter 플로그인은 안드로이드의 최소 SDK 버전이 20 이상 필요
     ...
 }
}</code></pre>
<p>마지막으로 http 허용을 위해 android/app/src/main/AndroidManifest.xml에서 아래와 같이 추가합니다.</p>
<pre><code class="language-xml">&lt;manifest xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
 package=&quot;com.example.blog_web_app&quot;&gt;
 &lt;uses-permission android:name=&quot;android.permission.INTERNET&quot; /&gt;
 &lt;application 
   ...         
   android:usesCleartextTraffic=&quot;true&quot;&gt;
   ...
 &lt;/application&gt;
&lt;/manifest&gt;</code></pre>
<h4 id="ios">iOS</h4>
<p>ios/Runner/Info.plist 파일에서 http 프로토콜을 허용하는 설정을 해줍니다.</p>
<pre><code class="language-plist">&lt;dict&gt;
&lt;key&gt;NSAppTransportSecurity&lt;/key&gt;
  &lt;dict&gt;
    &lt;key&gt;NSAllowsLocalNetworking&lt;/key&gt;
    &lt;true/&gt;
    &lt;key&gt;NSAllowsArbitraryLoadsInWebContent&lt;/key&gt;
    &lt;true/&gt;
  &lt;/dict&gt;
&lt;/dict&gt;</code></pre>
</li>
</ul>
<h2 id="웹뷰를-활용한-블로그-코드">웹뷰를 활용한 블로그 코드</h2>
<h4 id="libscreenhome_screendart">lib/screen/home_screen.dart</h4>
<pre><code class="language-dart">import &#39;package:flutter/material.dart&#39;;
import &#39;package:webview_flutter/webview_flutter.dart&#39;;

class HomeScreen extends StatelessWidget {
  // 웹뷰 컨트롤러 생성
  WebViewController webViewController = WebViewController()
    // 해당 url로 웹뷰가 로드됩니다. Uri.parse를 사용하여 입력한 url 문자열이 Uri 객체로 변환됩니다.
    ..loadRequest(Uri.parse(&#39;https://blod.codefactory.ai&#39;))
    // js 실행을 허용합니다.
    ..setJavaScriptMode(JavaScriptMode.unrestricted);

  HomeScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(&#39;Code factory&#39;),
        backgroundColor: Colors.orange,
        // 가운데 정렬
        centerTitle: true,

        // AppBar에 액션 버튼을 추가할 수 있는 매개변수
        actions: [
          IconButton(
            onPressed: () {
              webViewController
                  .loadRequest(Uri.parse(&#39;https://blod.codefactory.ai&#39;));
            },
            icon: Icon(Icons.home),
          )
        ],
      ),
      body: WebViewWidget(
        controller: webViewController,
      ),
    );
  }
}</code></pre>
<h4 id="libmaindart">lib/main.dart</h4>
<pre><code class="language-dart">import &#39;package:blog_web_app/screen/home_screen.dart&#39;;
import &#39;package:flutter/material.dart&#39;;

void main() {
  // 플러터 프레임워크가 앱을 실행할 준비가 될 때까지 기다림
  WidgetsFlutterBinding.ensureInitialized();

  runApp(
    MaterialApp(
      home: HomeScreen(),
    ),
  );
}
</code></pre>
<blockquote>
<p>《Must Have 코드팩토리의 플러터 프로그래밍 2판》의 스터디 내용 입니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[플러터] 7. 앱을 만들려면 알아야 하는 그 밖의 지식]]></title>
            <link>https://velog.io/@jun_/%ED%94%8C%EB%9F%AC%ED%84%B0-7.-%EC%95%B1%EC%9D%84-%EB%A7%8C%EB%93%A4%EB%A0%A4%EB%A9%B4-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%98%EB%8A%94-%EA%B7%B8-%EB%B0%96%EC%9D%98-%EC%A7%80%EC%8B%9D</link>
            <guid>https://velog.io/@jun_/%ED%94%8C%EB%9F%AC%ED%84%B0-7.-%EC%95%B1%EC%9D%84-%EB%A7%8C%EB%93%A4%EB%A0%A4%EB%A9%B4-%EC%95%8C%EC%95%84%EC%95%BC-%ED%95%98%EB%8A%94-%EA%B7%B8-%EB%B0%96%EC%9D%98-%EC%A7%80%EC%8B%9D</guid>
            <pubDate>Wed, 21 Feb 2024 11:22:56 GMT</pubDate>
            <description><![CDATA[<p>이번 장은 플러터 개발자로서 알로 있으면 좋은 지식들을 살펴봅니다.</p>
<h2 id="앱-만들기-프로세스">앱 만들기 프로세스</h2>
<p>현업에서는 보통 기획 -&gt; UI 구상하기 -&gt; 구현하기 -&gt; 테스트 순서로 만듭니다.
구현할 때는 폴더 구조를 잘 잡아야 협업이 편하고 유지보수가 용이합니다.
이 책은 다음과 같은 폴더구조를 소개합니다.</p>
<pre><code>.
├── screen
├── component
├── model
└── const</code></pre><ul>
<li>screen: 스크린 전체에 해당되는 위젯을 모아두는 폴더</li>
<li>component: 스크린을 구성하는 데 공통으로 사용될 만한 요소의 위젯을 모아두는 폴더</li>
<li>model: 모델들을 따로 모아두는 폴더</li>
<li>const: 상수들을 모아두는 폴더</li>
</ul>
<h2 id="플러그인-추가-방법">플러그인 추가 방법</h2>
<p>개발 시 보통 오픈소스를 불러와서 사용하게 됩니다.
플러그인을 추가하기 위해선 pubspec.yaml 파일에 원하는 플러그인을 추가하고 [pub get] 버튼을 눌러주면 됩니다.</p>
<pre><code class="language-yaml">dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^1.0.2
  webview_flutter: 2.3.1 // 웹뷰 플러그인 추가</code></pre>
<h2 id="주변-장치-종류">주변 장치 종류</h2>
<p>플러터는 다양한 하드웨어 기능을 제공해줍니다.
대표적으로 센서, GPS, 카메라, 블루투스, 와이파이 등이 있습니다.</p>
<h2 id="번외-로딩-화면-만들어보기">(번외) 로딩 화면 만들어보기</h2>
<p align="center">
<img src="https://velog.velcdn.com/images/jun_/post/0d4b4123-0813-4181-b41b-4d6bd329147d/image.png" width="300">
</p>

<pre><code class="language-dart">import &#39;package:flutter/material.dart&#39;;

void main() {
  runApp(SplashScreen());
}

class SplashScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        // Container로 전체를 감싸고 색상을 입힌다.
        body: Container(
          decoration: BoxDecoration(
            color: Color(0xFFF99231),
          ),
          // 가로 중앙 정렬을 위해 Row를 사용하고 mainAxisAlignment를 center로 설정한다.
          child: Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              // 아이콘과 로딩 애니메이션을 세로로 배치하기 위해 Column을 사용한다.
              Column(
                // 세로 중앙 정렬을 수행한다.
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Image.asset(
                    &#39;assets/logo.png&#39;,
                    width: 200,
                  ),
                  // 원형 로딩 애니메이션
                  CircularProgressIndicator(
                    // 로딩에 흰색을 입힌다.
                    valueColor: AlwaysStoppedAnimation(
                      Colors.white,
                    ),
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }
}
</code></pre>
<blockquote>
<p>《Must Have 코드팩토리의 플러터 프로그래밍 2판》의 스터디 내용 입니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[플러터] 6. 기본 위젯 알아보기]]></title>
            <link>https://velog.io/@jun_/%ED%94%8C%EB%9F%AC%ED%84%B0-6.-%EA%B8%B0%EB%B3%B8-%EC%9C%84%EC%A0%AF-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-dwccx1or</link>
            <guid>https://velog.io/@jun_/%ED%94%8C%EB%9F%AC%ED%84%B0-6.-%EA%B8%B0%EB%B3%B8-%EC%9C%84%EC%A0%AF-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0-dwccx1or</guid>
            <pubDate>Wed, 21 Feb 2024 01:22:37 GMT</pubDate>
            <description><![CDATA[<h2 id="위젯-소개">위젯 소개</h2>
<p>플러터는 화면에 그려지는 모든 요소가 위젯으로 구성되어 있습니다.
위젯은 현재 주어진 상태(state)를 기반으로 어떤 UI를 구현할지를 정의합니다.
상태가 변경되면 변경 사항에 알맞게 UI를 화면에 다시 그려줍니다.
위젯은 개발자가 직접 만들수도 있습니다.</p>
<p>위젯은 자식을 하나만 갖는 위젯과 여럿 갖는 위젯으로 나뉩니다.
자식을 하나만 갖는 대표적인 위젯을 다음과 같습니다.</p>
<ul>
<li>Container: 자식을 담는 컨테이너 역할을 합니다. 배경색, 너비와 높이, 테두리 등을 디자인할 수 있습니다.</li>
<li>GestureDetector: 플러터에서 제공하는 제스처 기능을 자식 위젯에서 인식하는 위젯입니다.</li>
<li>SizedBox: 높이와 너비를 지정하는 위젯입니다.</li>
</ul>
<p>자식을 여럿 갖는 대표적인 위젯은 다음과 같습니다.</p>
<ul>
<li>Column: children 매개변수에 입력된 모든 위젯들을 세로로 배치합니다.</li>
<li>Row 위젯: children 매개변수에 입력된 모든 위젯들을 가로로 배치합니다.</li>
<li>ListView: 리스트를 구현할 때 사용합니다. 스크롤도 가능합니다.</li>
</ul>
<p>이외에도 플러터에서 기본으로 제공하는 위젯들은 공식 웹사이트에서 확인할 수 있습니다.
<a href="https://flutter.dev/docs/development/ui/widgets">https://flutter.dev/docs/development/ui/widgets</a></p>
<p>이제 위에서 몇가지 위젯을 상세히 살펴보겠습니다.</p>
<h2 id="텍스트-관련-위젯">텍스트 관련 위젯</h2>
<pre><code class="language-dart">Text(
  &#39;hello world&#39;,
  style: TextStyle(
    fontSize: 16.0,
    fontWeight: FontWeight.w700,
    color: Colors.blue,
  ),
)</code></pre>
<h2 id="제스처-관련-위젯">제스처 관련 위젯</h2>
<h3 id="button">Button</h3>
<pre><code class="language-dart">TextButton(
  onPressed: () =&gt; {},
  child: Text(
   &#39;Click Me&#39;,
  ),
),</code></pre>
<h3 id="gesturedetector">GestureDetector</h3>
<pre><code class="language-dart">GestureDetector(
  onTap: () =&gt; {
    print(&#39;tab&#39;);
  },
  onLongPress: () =&gt; {
    print(&#39;long press&#39;);
  },
  child: Container(
    decoration: BoxDecoration(
      color: Colors.red
    ),
    width: 100.0,
    height: 100.0
  ),
),</code></pre>
<h3 id="floatingactionbutton">floatingActionButton</h3>
<pre><code class="language-dart">Scaffold(
  floatingActionButton: FloatingActionButton(
    onPressed: () {},
    child: Text(
      &#39;클릭&#39;,
    ),
  ),
  body: Container(),
),</code></pre>
<h2 id="디자인-관련-위젯">디자인 관련 위젯</h2>
<h3 id="container">Container</h3>
<pre><code class="language-dart">Container(
  decoration: BoxDecoration(
    color: Colors.red,
    border: Border.all(
      width: 16.0,
      color: Colors.black,
    ),
    borderRadius: BorderRadius.circular(
      16.0,
    ),
    height: 200.0,
    width: 100.0,
  ),
),</code></pre>
<h3 id="sizedbox">SizedBox</h3>
<pre><code class="language-dart">SizedBox(
  height: 200.0,
  width: 200.0,
  child: Container(
    color: Colors.red,
  ),
),</code></pre>
<h3 id="padding">Padding</h3>
<pre><code class="language-dart">Container(
  color: Colors.blue,
  child: Padding(
    padding: EdgeInsets.all(
      16.0,
    ),
    child: Container(
      color: Colors.red,
      width: 50.0,
      height: 50.0,
    ),
  ),
),</code></pre>
<h3 id="safearea">SafeArea</h3>
<p>현대 스마트폰은 크기와 디자인 모두 여러 가지입니다. 특히 아이폰의 노치 디자인은 특이합니다. 따라서 노치 등에 위젯이 가려지지 않기 위해 SafeArea를 사용하여 기기별로 예외 처리를 하지 않고 안전한 화면에서만 위젯을 그릴 수 있습니다.</p>
<pre><code class="language-dart">SafeArea(
  top: true,
  bottom: true,
  left: true,
  right: true,
  child: Container(
    color: Colors.red,
    height: 300.0,
    width: 300.0,
  ),
),</code></pre>
<h2 id="배치-관련-위젯">배치 관련 위젯</h2>
<h3 id="row">Row</h3>
<p>가로로 위젯을 배치할 때 사용합니다.</p>
<pre><code class="language-dart">SizedBox(
  // 반대축 (세로)에서 이동할 공간을 제공해주기 위해 높이를 최대한으로 설정
  height: double.infinity,
  child: Row(
    // 주축 정렬 지정
    mainAxisAlignment: MainAxisAlignment.start,
    // 반대축 정렬 지정
    crossAxisAlignment: CrossAxisAlignment.center,
    children: [
      Container(
        height: 50.0,
        width:50.0,
        color: Colors.red,
      ),
      // SizedBox는 일반적으로 공백을 생성할 때 사용
      const SizedBox(width: 12.0),
      Container(
        height: 50.0,
        width:50.0,
        color: Colors.green,
      ),
      const SizedBox(width: 12.0),
      Container(
        height: 50.0,
        width:50.0,
        color: Colors.blue,
      ),
    ],
  ),
),</code></pre>
<h3 id="column">Column</h3>
<p>세로로 위젯을 배치합니다.</p>
<pre><code class="language-dart">SizedBox(
  // 반대축 (가로)에서 이동할 공간을 제공해주기 위해 너비를 최대한으로 설정
  width: double.infinity,
  child: Column(
    // 주축 정렬 지정
    mainAxisAlignment: MainAxisAlignment.start,
    // 반대축 정렬 지정
    crossAxisAlignment: CrossAxisAlignment.center,
    children: [
      Container(
        height: 50.0,
        width:50.0,
        color: Colors.red,
      ),
      // SizedBox는 일반적으로 공백을 생성할 때 사용
      const SizedBox(width: 12.0),
      Container(
        height: 50.0,
        width:50.0,
        color: Colors.green,
      ),
      const SizedBox(width: 12.0),
      Container(
        height: 50.0,
        width:50.0,
        color: Colors.blue,
      ),
    ],
  ),
),</code></pre>
<h3 id="flexible">Flexible</h3>
<p>Row나 Column에서 사용하는 위젯입니다.
Flexible의 제공된 child가 크기를 최소한으로 차지하게 할 수 있습니다.
추가적으로 flow 매개변수를 이용해 각 Flexible 위젯이 얼만큼의 비율로 공간을 차지할지 지정할 수 있습니다.</p>
<pre><code class="language-dart">Column(
  children: [
    Flexible(
      flex: 3,
      child: Container(
        color: Colors.blue,
      ),
    ),
    Flexible(
      flex: 1,
      child: Container(
        color: Colors.red,
      ),
    ),
  ],
),</code></pre>
<h3 id="expanded">Expanded</h3>
<p>Flexible 위젯을 상속하는 위젯입니다. Column과 Row에서 Expanded를 사용하면 위젯이 남아 있는 공간을 최대한으로 차지합니다. 부모인 Flexible 위젯의 fit 매개변수에 FlexFit.tight가 디폴트로 지정됩니다.</p>
<pre><code class="language-dart">Column(
  children: [
    Expanded(
      child: Container(
        color: Colors.blue,
      ),
    ),
    Expanded(
      child: Container(
        color: Colors.red,
      ),
    ),
  ],
),</code></pre>
<h3 id="stack">Stack</h3>
<p>위젯을 겹치는 기능을 제공합니다.
플러터의 그래픽 엔진인 스키아 엔진은 2D 엔진으로 겹친 두께를 표현하진 못하지만 위젯 위에 위젯을 올린 듯한 효과를 줍니다.</p>
<pre><code class="language-dart">Stack(
  children: [
    Container(
      height: 300.0,
      width: 300.0,
      color: Colors.blue,
    ),
     Container(
      height: 250.0,
      width: 250.0,
      color: Colors.red,
    ),
     Container(
      height: 200.0,
      width: 200.0,
      color: Colors.green,
    ),
  ],
),</code></pre>
<blockquote>
<p>《Must Have 코드팩토리의 플러터 프로그래밍 2판》의 스터디 내용 입니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[플러터] 5. 플러터 입문하기]]></title>
            <link>https://velog.io/@jun_/%ED%94%8C%EB%9F%AC%ED%84%B0-5.-%ED%94%8C%EB%9F%AC%ED%84%B0-%EC%9E%85%EB%AC%B8%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@jun_/%ED%94%8C%EB%9F%AC%ED%84%B0-5.-%ED%94%8C%EB%9F%AC%ED%84%B0-%EC%9E%85%EB%AC%B8%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 19 Feb 2024 12:36:53 GMT</pubDate>
            <description><![CDATA[<h2 id="플러터-소개">플러터 소개</h2>
<p>플러터 프레임워크는 세 계층으로 나눠져 있습니다.</p>
<ul>
<li>Framework (dart)</li>
<li>Engine (c/c++)</li>
<li>Embedder (platform-sepcific)</li>
</ul>
<p>먼저 Framework 계층은 사용자들이 대부분의 시간을 보내는 곳입니다.
flutter 프레임워크를 의미하며 위젯, 애니메이션, material 패키지 등이 있습니다.</p>
<p>그리고 Engine 계층은 플러터 코어 API와 스키아 그래픽 엔진, 파일시스템 그리고 네트워크 기능 등이 정의되어 있습니다. 해당 엔진을 통해 플러터가 컴파일되고 UI를 그릴 수 있게됩니다.
다른 크로스플랫폼 앱 개발 프레임워크는 보통 웹뷰를 사용하거나 각 플랫폼의 UI 라이브러리를 사용합니다. 하지만 플러터는 직접 스키아 엔진을 사용하여 새로 렌더링이 필요한 위젯들만 렌더링 하기 때문에 높은 성능을 보입니다.</p>
<p>마지막으로 Embedder 계층은 현재 플러터가 지원하는 6개 플랫폼 (ios, android 등등)의 네이티브 플랫폼과 직접 통신을 하고 운영체제의 자체적 기능을 모듈화해둔 계층입니다.</p>
<h2 id="hello-world-앱-만들기">Hello World 앱 만들기</h2>
<p>먼저 CLI 환경에서 아래 명령어를 실행하여 flutter 프로젝트를 시작합니다.</p>
<pre><code class="language-shell">flutter create hello_world</code></pre>
<p>그리고 hello_world/lib 폴더 아래에 main.dart에 진입합니다.
자동으로 생성된 코드를 모두 삭제하고 아래와 같이 작성해봅니다.</p>
<pre><code class="language-dart">import &#39;package:flutter/material.dart&#39;;

void main() {
  runApp(
    const MaterialApp(
      home: Scaffold(
        body: Text(
          &#39;Hello world!&#39;,
        ),
      ),
    ),
  );
}</code></pre>
<p>MaterialApp은 Material 디자인 기반의 위젯들을 사용하게 해주는 위젯입니다.
Saffold는 Material 다음에 위치하는 위젯으로 화면 전체를 차지하며 레이아웃을 도와주고 UI 관련 특수 기능을 제공합니다. 예를 들어 화면에 알림과 같은 스낵바 등이 있습니다.
일반적으로 플러터에서 main 함수 안에 runApp을 실행하고 그 안에 MaterialApp과 Scaffold 위젯을 추가하는 것이 기본 설정이라고 생각하면 됩니다.</p>
<p>다음으로 화면 body에 &#39;Hello world!&#39;라는 Text를 추가하고 코드를 실행해봅니다.</p>
<pre><code>flutter run</code></pre><blockquote>
<p>《Must Have 코드팩토리의 플러터 프로그래밍 2판》의 스터디 내용 입니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[플러터] 4. 다트 3.0 신규 문법]]></title>
            <link>https://velog.io/@jun_/%ED%94%8C%EB%9F%AC%ED%84%B0-4.-%EB%8B%A4%ED%8A%B8-3.0-%EC%8B%A0%EA%B7%9C-%EB%AC%B8%EB%B2%95</link>
            <guid>https://velog.io/@jun_/%ED%94%8C%EB%9F%AC%ED%84%B0-4.-%EB%8B%A4%ED%8A%B8-3.0-%EC%8B%A0%EA%B7%9C-%EB%AC%B8%EB%B2%95</guid>
            <pubDate>Mon, 19 Feb 2024 11:59:42 GMT</pubDate>
            <description><![CDATA[<p>다트 언어 3.0 버전 이상부터 새로운 문법이 추가되었습니다.</p>
<h2 id="레코드">레코드</h2>
<pre><code class="language-dart">void main() {
  (String, int) minji = (&#39;민지&#39;, 20);
  print(minji); // (&#39;민지&#39;, 20)
  print(minji.$1); // 민지
  print(minji.$2); // 20
}</code></pre>
<p>네임드 파라미터를 이용하여 레코드를 정의할 수 있습니다.</p>
<pre><code class="language-dart">void main() {
  ({String name, int age}) minji = (name: &#39;민지&#39;, age: 20);
}</code></pre>
<h2 id="구조-분해">구조 분해</h2>
<pre><code class="language-dart">void main() {
  final [minji, haerin] = [&#39;민지&#39;, &#39;해린&#39;];
  final [x, y, ..., z] = [1, 2, 3, 4, 5, 6, 7, 8, 9]; // x: 1, y: 2, z: 9
  final {&#39;name&#39;: name, &#39;age&#39;: age} = {&#39;name&#39;: &#39;민지&#39;, &#39;age&#39;: 20}; // name: 민지, age: 20
  final Idol(name: name, age: age) = Idol(name: &#39;민지&#39;, age: 20); // name: 민지, age: 20
}</code></pre>
<h2 id="switch-문">switch 문</h2>
<p>3.0 이상부터 스위치 표현식, 패턴 매칭, 엄격한 검사, 보호 구문 네가지가 추가되었습니다.</p>
<h3 id="표현식-기능">표현식 기능</h3>
<pre><code class="language-dart">void main() {
  String dayKor = &#39;월요일&#39;;

  String dayEnglish = switch (dayKor) {
    &#39;월요일&#39; =&gt; &#39;Monday&#39;,
    &#39;화요일&#39; =&gt; &#39;Tuesday&#39;,
    _ =&gt; &#39;Not Found&#39;,
  };

  print(dayEnglish); // Monday
}</code></pre>
<h3 id="패턴-매칭">패턴 매칭</h3>
<pre><code class="language-dart">void switcher(dynamic anything) {
  switch (anything) {
    // 정확히 a만 매칭
    case &#39;a&#39;:
      print(&#39;match: a&#39;);
      break;
    // 원소개 3개인 배열을 매칭  
    case [_, _, _]:
      print(&#39;match [_, _, _]&#39;);
      break;
    // 두 원소가 모두 int인 배열을 매칭
    case: [int a, int b]:
      print(&#39;match [int, int]&#39;);
      break;
    default:
      print(&#39;default&#39;);
  }
}</code></pre>
<h3 id="엄격한-검사">엄격한 검사</h3>
<p>3.0부터는 코드가 입력받을 수 있는 모든 조건을 전부 확인하고 있는지 체크하는 기술입니다.</p>
<pre><code class="language-dart">void main() {
  bool? val;

  // null 조건이 없기 때문에 에러 발생
  switch(val) {
    case true:
      print(&#39;true&#39;);
    case false:
      print(&#39;false);
  }
}</code></pre>
<h3 id="보호-구문">보호 구문</h3>
<p>switch문에는 when 키워드로 보호 구문을 추가할 수 있도록 업데이트되었습니다.
when 키워드는 boolean으로 반환할 조건을 각 case문에 추가할 수 있으며 when 키워드 뒤에 오는 조건이 true를 반환하지 않으면 case 매치가 안됩니다.</p>
<pre><code class="language-dart">void main() {
  (int a, int b) val = (1, -1);

  switch (val) {
    case (1, _) when val.$2 &gt; 0:
      print(&#39;1, _&#39;);
      break;
    default:
      print(&#39;default&#39;);
  }
} </code></pre>
<h2 id="클래스-제한자">클래스 제한자</h2>
<p>다트 3.0 버전에는 base, final, interface, sealed, mixin 제한자가 추가되었습니다. 클래스 제한자를 명시한 클래스는 해당 클래스를 사용하는 파일일 아닌 다른 파일에서 선언해야 정상적으로 작동합니다.</p>
<h3 id="base-제한자">base 제한자</h3>
<p>base 제한자는 클래스의 기능을 강제하는 제한자입니다.
base 키워드를 사용한 클래스는 오직 상속만 가능합니다.
그리고 자식 클래스는 꼭 base, final, sealed 중 하나의 제한자와 함께 사용해야 합니다.</p>
<pre><code class="language-dart">// a.dart
base class Parent{}</code></pre>
<pre><code class="language-dart">// b.dart
import &#39;a.dart&#39;;

Parent parent = Parent();

base class Child extends Parent{}

// 에러가 발생합니다. (base, sealed, final 제한자 중 하나가 필요합니다.)
class Child2 extends Parent{}
// base 클래스는 implements가 불가능합니다.
class Child3 implements Parent{}</code></pre>
<h3 id="final-제한자">final 제한자</h3>
<p>final 제한자는 상속과 implements를 할 수 있지만 외부 파일에서는 할 수 없습니다.
그리고 base의 모든 기능을 포함합니다. (하위 자식들이 제한자가 강제되는 것도)</p>
<pre><code class="language-dart">// a.dart
final class Parent{}</code></pre>
<pre><code class="language-dart">// b.dart
impor &#39;a.dart&#39;;

Parent parent = Parent();
// 불가능
class Child extends Parent{}
class Child implements Parent{}</code></pre>
<h3 id="interface-제한자">interface 제한자</h3>
<p>interface 제한자는 implements만 할 수 있습니다.</p>
<pre><code class="language-dart">// a.dart
interface class Parent{}</code></pre>
<pre><code class="language-dart">// b.dart
import &#39;a.dart&#39;;

// 인스턴스화 가능
Parent parent = Parent();

class Child implements Parent{}</code></pre>
<h3 id="sealed-제한자">sealed 제한자</h3>
<p>sealed는 파일 외부에서 상속, implements, 인스턴스화 모두 불가능합니다.</p>
<h3 id="mixin-제한자">mixin 제한자</h3>
<pre><code class="language-dart">mixin class MixinExample{}

class Child with MixinExample{}
// 상속도 가능
class Child2 extends MinxinExample{}
</code></pre>
<blockquote>
<p>《Must Have 코드팩토리의 플러터 프로그래밍 2판》의 스터디 내용 입니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[플러터] 3. 다트 비동기 프로그래밍]]></title>
            <link>https://velog.io/@jun_/3.-%EB%8B%A4%ED%8A%B8-%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</link>
            <guid>https://velog.io/@jun_/3.-%EB%8B%A4%ED%8A%B8-%EB%B9%84%EB%8F%99%EA%B8%B0-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</guid>
            <pubDate>Mon, 12 Feb 2024 11:39:17 GMT</pubDate>
            <description><![CDATA[<p>이번장은 다트 언어를 사용해 비동기 프로그래밍을 하는 방법에 대해 소개합니다.</p>
<h2 id="future">Future</h2>
<p>Future 클래스를 이용하여 미래에 받아올 값을 정의합니다.</p>
<pre><code class="language-dart">Future&lt;String&gt; name;</code></pre>
<p>특정 기간동안 아무것도 하지 않고 기다리는 Future.delayed()를 사용하여 예시를 보겠습니다.</p>
<pre><code class="language-dart">void main() {
  addNumbers(1, 1);
}

void addNumber(int num1, int num2) {
  print(&#39;$num1 + $num2 계산 시작!&#39;);
  Future.delayed(Duration(seconds: 3), () =&gt; {
    print(&#39;$num1 + $num2 = ${num1 + num2}&#39;);
  })
  print(&#39;코드 실행 끝&#39;);
}</code></pre>
<pre><code>1 + 1 계산 시작!
코드 실행 끝
1 + 1 = 2</code></pre><h2 id="async와-await">async와 await</h2>
<pre><code class="language-dart">void main() {
  addNumbers(1, 1);
}

Future&lt;void&gt; addNumber(int num1, int num2) async {
  print(&#39;$num1 + $num2 계산 시작!&#39;);
  await Future.delayed(Duration(seconds: 3), () =&gt; {
    print(&#39;$num1 + $num2 = ${num1 + num2}&#39;);
  })
  print(&#39;코드 실행 끝&#39;);

  return num1 + num2;
}

/* 결과
1 + 1 계산 시작!
1 + 1 = 2
코드 실행 끝
*/</code></pre>
<pre><code class="language-dart">void main() {
  addNumbers(1, 1);
  addNumbers(2, 2);
}

/* 결과
1 + 1 계산 시작!
2 + 2 계산 시작!
1 + 1 = 2
코드 실행 끝
2 + 2 = 4
코드 실행 끝
*/</code></pre>
<pre><code class="language-dart">void main() async {
  await addNumber(1, 1);
  await addNumber(2, 2);
}

/* 결과
1 + 1 계산 시작!
1 + 1 = 2
코드 실행 끝
2 + 2 계산 시작!
2 + 2 = 4
코드 실행 끝
*/</code></pre>
<h3 id="결괏값-반환받기">결괏값 반환받기</h3>
<pre><code class="language-dart">void main() async {
  final result = await addNumber(1, 1); // 2
}</code></pre>
<h3 id="futurewait">Future.wait()</h3>
<p>Future로 구성된 리스트를 매개변수로 입력받습니다.
해당 비동기 함수들은 모두 동시에 실행되며 응답값을 요청을 보낸 순서대로 저장합니다.
(js의 promise.all과 유사)</p>
<h2 id="stream">Stream</h2>
<p>Future는 반환값을 딱 한 번 받아내는 비동기 프로그래밍에 사용됩니다.
지속적으로 값을 반환 받을 때는 Stream을 사용합니다.</p>
<h3 id="기본-사용법">기본 사용법</h3>
<p>사용하기 위해선 플러터에서 기본으로 제공하는 dart:async 패키지를 불러와야합니다.</p>
<pre><code class="language-dart">import &#39;dart:async&#39;;

void main() {
  final controller = StreamController(); // StreamController 선언
  final stream = controller.stream; // Stream 가져오기
  // stream의 listen() 함수를 실행하면 값이 주입될 때마다 콜백 함수를 실행할 수 있습니다.
  final streamListener = stream.listen((val) =&gt; {
    print(val);
  });

  controller.sink.add(1); // 1 출력
  controller.sink.add(2); // 2 출력
}</code></pre>
<h3 id="브로드캐스트-스트림">브로드캐스트 스트림</h3>
<p>Stream은 단 한 번만 listen()을 실행할 수 있습니다.
반면에 브로드캐스트 스트림을 사용하면 Stream을 여러번 listen() 하도록 변환할 수 있습니다.</p>
<pre><code class="language-dart">import &#39;dart:async&#39;;

void main() {
  final controller = StreamController();
  final stream = controller.stream.asBroadcastStream();

  final streamListener1 = stream.listen((val) =&gt; {
    print(&#39;listener1 $val&#39;);
  });

  final streamListener2 = stream.listen((val) =&gt; {
    print(&#39;listener2 $val&#39;);
  })

  controller.sink.add(1);
  controller.sink.add(2);

  /*결과
  listener1 1
  listener2 1
  listener1 2
  listener2 2
  */
}</code></pre>
<h3 id="함수로-stream-반환하기">함수로 Stream 반환하기</h3>
<p>StreamController를 사용하지 않고도 Stream을 반환하는 함수를 작성할 수 있습니다.</p>
<pre><code class="language-dart">import &#39;dart:async&#39;;

// Stream을 반환하는 함수는 async*로 선언합니다.
Stream&lt;String&gt; countDown(int number) async* {
  for (int i=number; i&gt;0; i--) {
    yield &#39;$i&#39;;
    await Future.delayed(Duration(seconds:1));
  }
}

void playStream() {
  countDown(5).listen((val) =&gt; {
    print(val);
  });
}

void main() {
  playStream();
}

/*결과
5
4
3
2
1
*/</code></pre>
<blockquote>
<p>《Must Have 코드팩토리의 플러터 프로그래밍 2판》의 스터디 내용 입니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[플러터] 2. 다트 객체지향 프로그래밍]]></title>
            <link>https://velog.io/@jun_/%ED%94%8C%EB%9F%AC%ED%84%B0-2.-%EB%8B%A4%ED%8A%B8-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</link>
            <guid>https://velog.io/@jun_/%ED%94%8C%EB%9F%AC%ED%84%B0-2.-%EB%8B%A4%ED%8A%B8-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</guid>
            <pubDate>Sun, 11 Feb 2024 13:13:19 GMT</pubDate>
            <description><![CDATA[<p>이번 장은 다트 언어로 객체지향 프로그래밍에 대해 소개한다.</p>
<h2 id="클래스">클래스</h2>
<pre><code class="language-dart">class Idol {
  String name = &#39;블랙핑크&#39;;

  void sayName() {
    print(&#39;저는 ${this.name}입니다.&#39;);
    print(&#39;저는 $name입니다.&#39;); // 스코프안에 name이 하나만 있다면 this 생략 가능
  }
}

void main() {
  Idol blackPink = Idol();
  blackPink.sayName();
}</code></pre>
<h3 id="생성자">생성자</h3>
<p>생성자에서 입력받을 변수를 일반적으로 final로 선언합니다.
인스턴스화한 다음에 혹시라도 변수의 값을 변경하는 실수를 막기 위함입니다.
생성자는 클래스와 같은 이름으로 선언합니다.</p>
<pre><code class="language-dart">class Idol {
  final String name;

  Idol(String name) : this.name = name;
  // Idol(this.name); this를 사용할 경우 해당되는 변수에 자동으로 저장됩니다. 

  void sayName() {
    print(&#39;저는 ${this.name}입니다.&#39;);
  }
}

void main() {
  Idol blackPick = Idol(&#39;블랙핑크&#39;);
  blackPink.sayName();
}</code></pre>
<h3 id="네임드-생성자">네임드 생성자</h3>
<p>네임드 생정자는 네임드 파라미터와 비슷한 개념입니다.
일반적으로 클래스를 생성하는 여러 방법을 명시하고 싶을 때 사용합니다.
아래 예시는 일반적인 생성자와 Map을 파라미터로 받는 생성자를 보여줍니다.</p>
<pre><code class="language-dart">class Idol {
  final String name;
  final int memberCount;

  Idol(String name, int memberCount)
    : this.name = name, // , 기호로 여러 변수를 저장할 수 있습니다.
      this.memberCount = memberCount;  
  // Idol (this.name, this.memberCount);

  Idol.fromMap(Map&lt;String, dynamic&gt; map)
    : this.name = map[&#39;name&#39;],
      this.memberCount = map[&#39;memberCount&#39;];

  void sayName() {
    print(&#39;이름은 $name이고 $memberCount명입니다.&#39;);
  }
}

void main() {
  Idol blackPink = Idol(&#39;블랙핑크&#39;, 4);
  blackPink.sayName();

  Idol bts = Idol.fromMap({
    &#39;name&#39;: &#39;BTS&#39;,
    &#39;memberCount&#39;: 7,
  });
  bts.sayName(); // 이름은 BTS이고 7명입니다.
}</code></pre>
<h3 id="프라이빗-변수">프라이빗 변수</h3>
<p>다트에서 프라이빗 변수는 클래스 내부에서만 사용하는 변수가 아닌, <strong>같은 파일에서만 접근 가능한 변수입니다</strong>.</p>
<pre><code class="language-dart">class Idol {
  String _name;

  Idol(this._name);
}

void main() {
  Idol blackPink = Idol(&#39;블랙핑크&#39;);
  print(blackPink._name); // 같은 파일에서는 _name에 접근 가능하지만, 다른 파일에선 불가능 합니다.
}</code></pre>
<h3 id="getter--setter">getter / setter</h3>
<pre><code class="language-dart">class Idol {
  String _name;

  Idol(this._name);

  String get name {
    return this._name;
  }

  set name(String name) {
    this._name = name;
  }
}

void main() {
  Idol blackPink = Idol(&#39;블랙핑크&#39;);
  blackPink.name = &#39;에이핑크&#39;;
  print(blackPick.name); // 에이핑크
}</code></pre>
<h3 id="상속">상속</h3>
<pre><code class="language-dart">class Idol {
  final String name;

  Idol(this.name);

  void sayName() {
    print(&#39;저는 $name입니다.&#39;);
  }
}

class BoyGroup extends Idol {
  BoyGroup(String name) : super(name);
  // BoyGroup(super.name);

  void sayMale() {
    print(&#39;저는 남자 아이돌입니다.&#39;);
  }
}

void main() {
  BoyGroup bts = BoyGroup(&#39;BTS&#39;);
  bts.sayName();
  bts.sayMale();
}</code></pre>
<h4 id="오버라이드">오버라이드</h4>
<pre><code class="language-dart">class GirlGroup extends Idol {
  GirlGroup(super.name);

  void sayName() {
    print(&#39;저는 여자 아이돌 $name입니다.&#39;);
  }
}

void main() {
  GirlGroup blackPink = GirlGroup(&#39;블랙핑크&#39;);
  blackPink.sayName(); // 저는 여자 아이돌 블랙핑크입니다.
}</code></pre>
<h3 id="인터페이스">인터페이스</h3>
<p>인터페이스는 변수, 메서드의 이름만 같고 모든 기능은 새로 다시 정의해주어야 합니다.
인터페이스는 특정 클래스가 반드시 같은 이름을 같는 변수와 메서드를 사용해야할 때 유용합니다.</p>
<pre><code class="language-dart">class Idol {
  final String name;

  void sayName() {}
}

class GirlGroup implements Idol {
  final String name;

  GirlGroup (this.name);

  void sayName() {
    print(&#39;저는 여자 아이돌 $name입니다.&#39;);
  }
}

void main() {
  GirlGroup blackPink = GirlGroup(&#39;블랙핑크&#39;);
  blackPink.sayName();
}</code></pre>
<h3 id="믹스인">믹스인</h3>
<p>믹스인은 특정 클래스에 원하는 기능들만 골라넣을 수 있는 기능입니다.</p>
<pre><code class="language-dart">mixin IdolSingMixin on Idol {
  void sing() {
    print(&#39;$name이 노래를 부릅니다.&#39;);
  }
}

class BoyGroup extends Idol with IdolSingMixin {
  BoyGroup(super.name);

  void sayMale() {
    print(&#39;저는 남자 아이돌입니다.&#39;);
  }
}

void main() {
  BoyGroup bts = BoyGroup(&#39;BTS&#39;);
  bts.sing();
}</code></pre>
<h3 id="추상-클래스">추상 클래스</h3>
<p>추상 클래스는 변수, 메서드 이름만 정의되어 있고, implements나 extends를 통해 기능을 정의하거나 추가합니다.</p>
<pre><code class="language-dart">abstract class Idol {
  final String name;

  Idol(this.name);

  void sayName();
}

class GirlGroup implements Idol {
  final String name;

  GirlGroup(this.name);

  void sayName() {
    print(&#39;$name입니다.&#39;);
  }
}

void main() {
  GirlGroup blackPink = GirlGroup(&#39;블랙핑크&#39;);
  blackPink.sayName();
}</code></pre>
<h3 id="제네릭">제네릭</h3>
<p>제네릭은 클래스나 함수의 정의를 선언할 때가 아니라 인스턴스화 하거나 실행할 때로 미룹니다.
특정 변수의 타입을 하나의 타입으로 제한하고 싶지 않을 때 자주 사용합니다.</p>
<pre><code class="language-dart">class Cache&lt;T&gt; {
  final T data;

  Cache({
    required this.data,
  });
}

void main() {
  final cache = Cache&lt;List&lt;int&gt;&gt;(
    data: [1, 2, 3],
  );

  print(cache.data.reduce((v, e) =&gt; v + e)); // 6
}</code></pre>
<h3 id="static">Static</h3>
<p>static은 인스턴스끼리 공유해야하는 정보를 위해 사용합니다.</p>
<pre><code class="language-dart">class Counter {
  static int i = 0;

  Counter() {
    print(i++);
  }
}

void main() {
  Counter count1 = Counter(); // 1
  Counter count2 = Counter(); // 2
}</code></pre>
<h3 id="캐스케이드-연산자">캐스케이드 연산자</h3>
<p>캐스케이드 연산자는 인스턴스에서 해당 인스턴스의 속성이나 멤버 함수를 연속해서 사용하는 기능입니다.
... 기호를 사용합니다.</p>
<pre><code class="language-dart">void main() {
  Idol blackPink = Idol(&#39;블랙핑크&#39;)
    ...sayName(); // 저는 블랙핑크입니다.

  blackPick...sayName()...sayMemeberCount();
}</code></pre>
<blockquote>
<p>《Must Have 코드팩토리의 플러터 프로그래밍 2판》의 스터디 내용 입니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[플러터] 1. 다트 언어 마스터하기]]></title>
            <link>https://velog.io/@jun_/%ED%94%8C%EB%9F%AC%ED%84%B0-1.-%EB%8B%A4%ED%8A%B8-%EC%96%B8%EC%96%B4-%EB%A7%88%EC%8A%A4%ED%84%B0%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@jun_/%ED%94%8C%EB%9F%AC%ED%84%B0-1.-%EB%8B%A4%ED%8A%B8-%EC%96%B8%EC%96%B4-%EB%A7%88%EC%8A%A4%ED%84%B0%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sat, 10 Feb 2024 04:02:03 GMT</pubDate>
            <description><![CDATA[<p>1장에서는 다트 언어의 기본 문법에 대해 설명합니다.</p>
<h2 id="메인-함수">메인 함수</h2>
<p>다트는 프로그램 시작점인 엔트리 함수 기호로 main()을 사용합니다.</p>
<pre><code class="language-dart">void main() {}</code></pre>
<h2 id="주석">주석</h2>
<p>주석 기호로 //, /**/, /// 를 사용합니다.</p>
<pre><code class="language-dart">void main() {
  // 한 줄 주석

  /*
  * 여러 줄 주석
  * 여러 줄 주석
  */

  /// 문서 주석
}</code></pre>
<h2 id="출력">출력</h2>
<pre><code class="language-dart">void main() {
  print(&#39;Hello World&#39;);
}</code></pre>
<h2 id="변수-선언">변수 선언</h2>
<p>변수 선언 방법으로 다음과 같이 4가지가 있습니다.</p>
<ul>
<li>var</li>
<li>dynamic</li>
<li>final/const</li>
<li>타입</li>
</ul>
<h3 id="var">var</h3>
<p>변수에 값이 들어가면 자동으로 타입을 추론하는 방법입니다.
타입이 추론된 이후에는 다른 타입을 변수에 저장할 수 없습니다.</p>
<pre><code class="language-dart">void main() {
  var name = &#39;코드팩토리&#39;; // string으로 추론
  name = &#39;골든래빗&#39;;
  name = 10; // error
}</code></pre>
<h3 id="dynamic">dynamic</h3>
<p>var와 다르게 변수의 타입이 고정되지 않으며 다른 타입의 값으로 변경할 수 있습니다.</p>
<pre><code class="language-dart">void main() {
  var name = &#39;코드팩토리&#39;;
  name = 10;
}</code></pre>
<h3 id="finalconst">final/const</h3>
<p>변수의 값을 처음 선언 후 변경할 수 없습니다.
final은 런타임 시간에 결정되는 상수이고, const는 빌드 시간에 결정되는 상수입니다.</p>
<pre><code class="language-dart">void main() {
  final DateTime now = DateTime.now(); // 런타임 때 현재 시간
}</code></pre>
<pre><code class="language-dart">void main() {
  const DateTime now = DateTime.now(); // 빌드 시점에 알 수 없음
}</code></pre>
<p>코드를 실행하지 않은 상태에서 값이 확정되면 const를, 실행될 때 확정되면 final을 사용하면 됩니다.</p>
<h3 id="타입">타입</h3>
<p>직접적으로 타입을 명시해주는 방법</p>
<pre><code class="language-dart">void main() {
  String name = &#39;이름&#39;;
  int intValue = 10;
  double doubleValue = 2.5;
  bool isTrue = true;
}</code></pre>
<h2 id="컬렉션">컬렉션</h2>
<p>여러 값을 하나의 변수에 저장할 수 있는 타입입니다.
List, Map, Set이 이에 속합니다.
다트의 컬렉션 타입은 서로의 타입으로 자유롭게 형변환이 가능하다는 장점이 있습니다.</p>
<h3 id="list-타입">List 타입</h3>
<pre><code class="language-dart">void main() {
  List&lt;String&gt; blackPinkList = [&#39;리사&#39;, &#39;지수&#39;, &#39;제니&#39;, &#39;로제&#39;];
}</code></pre>
<p>List타입에서 제공하는 함수가 많습니다. 그중 몇가지만 소개합니다.</p>
<ul>
<li>add(): 배열에 원소를 추가합니다.</li>
<li>where(): 배열을 필터링합니다. (js와 filter와 유사)</li>
<li>map(): 배열의 전체의 값을 변경하고 iterable로 반환합니다.. (js의 map과 유사)<ul>
<li>반환된 iterable을 List로 변경하려면 iterable.toList()를 실행합니다.</li>
</ul>
</li>
<li>reduce(): 배열을 하나의 값으로 반환합니다. 이때 배열의 원소 타입으로 반환합니다.</li>
<li>fold(): reduce와 유사하지만, 어떠한 타입이든 반환할 수 있습니다. (js의 reduce와 유사)</li>
</ul>
<h3 id="map-타입">Map 타입</h3>
<p>Map 타입은 키와 값의 짝을 저장합니다.</p>
<pre><code class="language-dart">void main () {
  Map&lt;String, String&gt; dictionary = {
    &#39;Harry Potter&#39;: &#39;해리 포터&#39;,
    &#39;Ron Weasley&#39;: &#39;론 위즐리&#39;,
  }

  print(dictionary.keys); // (Harry Potter, Ron Weasley)
  print(dictionary.values.toList()); // [해리 포터, 론 위즐리]
}</code></pre>
<h3 id="set-타입">Set 타입</h3>
<p>Set은 중복 없는 값들의 집합입니다.</p>
<pre><code class="language-dart">void main() {
  Set&lt;String&gt; blackpink = {&#39;로제&#39;, &#39;지수&#39;, &#39;지수&#39;};
  print(blackpink); // {로제, 지수}
  print(blackpink.toList); // [로제, 지수]
  print(blackpink.contains(&#39;로제&#39;)); // true

  List&lt;String&gt; blackpink2 = [&#39;로제&#39;, &#39;지수&#39;];
  Set.from(blackpink2) // List -&gt; Set 변환
}</code></pre>
<h3 id="enum-타입">enum 타입</h3>
<p>한 변수로 몇 가지 옵션을 제한할 때 사용할 수 있습니다.</p>
<pre><code class="language-dart">enum Status {
  approved,
  pending,
  rejected,
}

void main() {
  Status status = Status.approved;
}</code></pre>
<h2 id="연산자">연산자</h2>
<h3 id="산술-연산자">산술 연산자</h3>
<pre><code class="language-dart">void main() {
  double number = 2;

  print(number + 2);
  print(number - 2);
  print(number * 2);
  print(number / 2);
  print(number % 3);
  number++;
  number--;
  number += 2;
  number -= 2;
  number *= 2;
  number /= 2;
}</code></pre>
<h3 id="null-관련-연산자">null 관련 연산자</h3>
<p>다트에서는 변수 타입이 null값을 가지는지 여부를 직접 지정해줘야 합니다.</p>
<pre><code class="language-dart">void main() {
  double? number1 = 1;
  double? number2 = null;
  double number3 = null; // error
}</code></pre>
<p>?? 연산자를 사용하여 변수의 값이 null일 때만 값을 지정할 수 있습니다.</p>
<pre><code class="language-dart">void main() {
  double? number = null;
  number ??= 1;
  number ??= 2; // null이 아니기 때문에 1이 유지됩니다.
}</code></pre>
<h3 id="비교-연산자">비교 연산자</h3>
<pre><code class="language-dart">void main() {
  int num1 = 1;
  int num2 = 2;

  // 값 비교
  print(num1 &gt; num2);
  print(num1 &lt; num2);
  print(num1 &gt;= num2);
  print(num1 &lt;= num2);
  print(num1 == num2);
  print(num1 != num2);

  // 타입 비교
  print(num1 is int);
  print(num1 is String);
  print(num1 is! int);
  print(num1 is! String);

}</code></pre>
<h3 id="논리-연산자">논리 연산자</h3>
<pre><code class="language-dart">void main() {
  bool result1 = true &amp;&amp; false // false
  bool result2 = true || false // true
}</code></pre>
<h2 id="제어문">제어문</h2>
<h3 id="if문">if문</h3>
<pre><code class="language-dart">void main() {
  if () {
    ...
  } else if () {
    ...
  } else {
    ...
  }
}</code></pre>
<h3 id="switch-문">switch 문</h3>
<pre><code class="language-dart">void main() {
  int num = 1;
  switch(num) {
    case 1:
      break;
    case 2:
      break;
    default:
      ...
  }
}</code></pre>
<h3 id="for문">for문</h3>
<pre><code class="language-dart">void main() {
  for (int i = 0; i &lt; 3; i++) {
    print(i);
  }
}</code></pre>
<pre><code class="language-dart">void main() {
  List&lt;int&gt; numList = [1, 2, 3];
  for (int num in numList) {
    print(num);
  }
}</code></pre>
<h3 id="while문">while문</h3>
<pre><code class="language-dart">void main() {
  int total = 0;
  while(total &lt; 10) {
    total += 1;
  }
}</code></pre>
<pre><code class="language-dart">void main() {
  int total = 0;
  do {
    total += 1;
  }while(total &lt; 10)
}</code></pre>
<h2 id="함수와-람다">함수와 람다</h2>
<h3 id="함수">함수</h3>
<pre><code class="language-dart">int add(int a, int b) {
  return a + b;
}

void main() {
  int result = add(1, 2);
}</code></pre>
<p>다트는 네임드 파라미터를 통해 순서와 상관 없이 파라미터를 지정할 수 있습니다.
네임드 파라미터를 이용하려면 required와 중괄호 {}를 사용합니다.
여기서 required는 매개변수가 null 값이 불가능한 타입이면 기본값을 지정해주거나 필수로 입력해야 한다는 의미입니다.</p>
<pre><code class="language-dart">int add({required int a, required int b}) {
  return a + b;
}

void main() {
  int result = add(a: 1, b: 2);
}</code></pre>
<p>파라미터에 기본값을 지정해 줄 수 있습니다.</p>
<pre><code class="language-dart">int add(int a, [int b = 2]) {
 return a + b;
}

int add2({required int a; int b = 2}) {
 return a + b;
}

void main() {
  int result = add(1); // 3
  int result2 = add2(a: 1); // 3
}</code></pre>
<p>포지셔널 파라미터와 네임드 파라미터를 섞어서 사용할 수도 있습니다.</p>
<pre><code class="language-dart">int add(int a, {required int b, int c = 4}) {
 return a + b + c;
}

void main() {
 int result = add(1, b: 2, c: 3);
}</code></pre>
<h3 id="람다-함수">람다 함수</h3>
<pre><code class="language-dart">void main() {
  List&lt;int&gt; numbers = [1, 2, 3];

  final allMembers = numbers.reduce((v, el) =&gt; v + el);
}</code></pre>
<h3 id="typedef와-함수">typedef와 함수</h3>
<p>typedef는 함수의 시그니처를 정의하는 값으로, 함수를 타입으로 정의할 수 있습니다.</p>
<pre><code class="language-dart">typedef Operation = void Function(int x, int y);

void add (int x, int y) {
  print(x + y);
}

void calculate(int x, int y, Operation oper) {
  oper(x, y);
}

void main() {
  Operation oper = add;
  calculate(1, 2, oper);
}</code></pre>
<h2 id="trycatch">try...catch</h2>
<pre><code class="language-dart">void main() {
  try {
    final String name = &#39;hello&#39;;
    throw Exception(&#39;이름이 잘못됐습니다.&#39;);
  } catch(e) {
    print(e);
  }
}</code></pre>
<blockquote>
<p>《Must Have 코드팩토리의 플러터 프로그래밍 2판》의 스터디 내용 입니다.</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[C4 모델링]]></title>
            <link>https://velog.io/@jun_/C4-%EB%AA%A8%EB%8D%B8%EB%A7%81</link>
            <guid>https://velog.io/@jun_/C4-%EB%AA%A8%EB%8D%B8%EB%A7%81</guid>
            <pubDate>Mon, 30 May 2022 09:29:35 GMT</pubDate>
            <description><![CDATA[<h1 id="c4-모델">C4 모델</h1>
<p>C4 모델은 소프트웨어 아키텍처를 모델링하기 위한 표기법이다.
C4모델은 시스템 컨텍스트, 컨테이너, 컴포넌트, 코드 순서로 수준(level)을 나누어 소프트웨어를 작은 단위로 분해해가는 방식으로 모델링하는 기법이다.</p>
<ul>
<li><p>시스템 컨텍스트
  최상위 추상화 요소로 시스템이 어떤 가치를 제공하는지 나타내며 보유하거나 개발하고 있는 소프트웨어와 연동되는 소프트웨어를 나타낼 때 사용한다.</p>
</li>
<li><p>컨테이너
  소프트웨어 내부를 표현하는 추상화 요소로 서버와 클라이언트 데이터베이스 등이 어떻게 구성되어 있는지 나타낸다.</p>
</li>
<li><p>컴포넌트
  컨테이너의 각 요소들을 추상화 하는 단계로 기능 단위로 묶을 수 있는 모듈을 의미한다.</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[쿠키, 세션, 토큰]]></title>
            <link>https://velog.io/@jun_/%EC%BF%A0%ED%82%A4-%EC%84%B8%EC%85%98-%ED%86%A0%ED%81%B0</link>
            <guid>https://velog.io/@jun_/%EC%BF%A0%ED%82%A4-%EC%84%B8%EC%85%98-%ED%86%A0%ED%81%B0</guid>
            <pubDate>Sat, 28 May 2022 10:01:31 GMT</pubDate>
            <description><![CDATA[<h1 id="쿠키">쿠키</h1>
<h2 id="쿠키란">쿠키란?</h2>
<ul>
<li>쿠키는 브라우저 로컬에 저장되는 key, value로 구성된 데이터 파일이다.</li>
<li>쿠키를 이용하여 서버에서 브라우저에 데이터를 넣을 수 있다.</li>
<li>브라우저에 300개까지 쿠키 저장이 가능하고, 하나의 도메인당 20개의 값만 가질 수 있다.</li>
<li>하나의 쿠키는 최대 4KB이다.</li>
<li>쿠키는 서버에 요청을 보낼 때 request 헤더에 자동으로 넣어서 함께 전송하게 된다.</li>
</ul>
<h2 id="쿠키의-사용-예시">쿠키의 사용 예시</h2>
<ul>
<li>아이디, 비밀번호 저장, 자동 로그인</li>
<li>쇼핑몰의 장바구니</li>
</ul>
<h1 id="세션">세션</h1>
<p>HTTP 프로토콜은 stateless이다.
즉, 서버로 가는 모든 request는 독립적이며 request가 끝나면 서버는 해당 request 정보를 기억하지 않는다.
따라서 로그인 같은 상태 정보 유지를 위해서 세션을 사용한다.</p>
<p>사용자가 ID, PW를 입력한 뒤, 서버에 로그인을 요청하면 서버는 해당 요청을 기반으로 세션을 생성한다.</p>
<p>세션은 DB에 저장되고, 해당 세션의 ID를 쿠키에 담아 클라이언트로 전송한다.</p>
<p>이제 클라이언트는 해당 서버에 요청을 보낼 때마다 쿠키에 세션ID가 담겨있으므로 로그인 유지가 가능해진다.</p>
<h1 id="토큰">토큰</h1>
<p>세션을 통한 사용자 정보 유지 방법은 사용자가 많을 수록 DB에 저장되는 세션 정보가 늘어나기 때문에 DB 리소스가 더 필요해진다.</p>
<p>따라서 이러한 문제를 해결하기 위해 토큰 방식이 등장한다.</p>
<p>토큰 방식으로 JWT가 있다.</p>
<h2 id="jwt">JWT</h2>
<p>사용자가 로그인 요청을 보내면, 서버는 DB에 세션을 생성하지 않는다.
대신 서버는 사용자의 ID를 기반으로 사인 알고리즘을 통해 사인된 정보 &#39;토큰&#39;을 생성한다.</p>
<p>생성된 토큰은 클라이언트로 전송하게 되고, 클라이언트는 이제 서버에 요청을 보낼 때 해당 토큰을 담아 요쳥을 보내 사용자 정보를 유지할 수 있다.</p>
<h2 id="jwt의-저장-방법">JWT의 저장 방법</h2>
<p>JWT의 저장 방법으로 쿠키, localstorage, private variable이 있다.</p>
<p>먼저 private variable 저장 방식은 새로고침할 때마다 JWT 값이 사라지기 때문에 매번 로그인을 수행해야한다.</p>
<p>또, 쿠키와 localstorage 방식은 CSRF, XSS에 의해 보안에 취약하다.</p>
<p>그래서 가장 좋은 방법은 refresh token을 사용하는 것이다.
refresh token을 httpOnly 쿠키로 저장하고, 새로고침될 때마다 refresh token을 request에 담아 전송한다.
그리고 새로은 access token을 발급받는다.
발급 받은 access token은 private variable에 저장할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTTP 역사]]></title>
            <link>https://velog.io/@jun_/HTTP-%EC%97%AD%EC%82%AC</link>
            <guid>https://velog.io/@jun_/HTTP-%EC%97%AD%EC%82%AC</guid>
            <pubDate>Fri, 27 May 2022 07:41:44 GMT</pubDate>
            <description><![CDATA[<h1 id="http">HTTP</h1>
<h2 id="http-역사">HTTP 역사</h2>
<h3 id="http10">HTTP/1.0</h3>
<ul>
<li>HTTP/1.0은 기본적으로 한 연결당 하나의 요청을 처리하도록 설계되어있다.</li>
<li>따라서 하나의 요청을 처리하기 위해 매번 3-way handshaking 과정이 필요하다.</li>
<li>이는 RTT (패킷이 목적지에 도달하고 나서 다시 출발지로 돌아오는 왕복 시간)를 증가시킨다.</li>
<li>이를 해결하기 위해 다음과 같은 기법을 사용한다.<ul>
<li>이미지 스플리팅</li>
<li>코드 압축</li>
<li>이미지 Base64 인코딩</li>
</ul>
</li>
</ul>
<h3 id="http11">HTTP/1.1</h3>
<ul>
<li>매번 TCP 연결을 하는게 아니라 최초 한 번만 연결하고 여러 개의 파일을 계속 보낼 수 있다.</li>
<li>keep-alive 옵션을 이용한다.</li>
<li>하지만 파일은 순차적으로 전송 되므로, 하나의 파일에서 지연이 발생하면 다른 파일을 전송하는데 대기 시간이 길어진다.</li>
</ul>
<h3 id="http2">HTTP/2</h3>
<ul>
<li>HTTP/2는 파일을 순차적으로 전송하는 방식이 아닌 병렬 처리를 지원한다.</li>
<li>1.1은 헤더가 큰 문제가 있었는데, 이를 헤더 압축을 하면서 해결한다.</li>
<li>또한 1.1은 클라이언트가 요청을 보내야 서버에서 파일을 전송하는데, 2에서는 클라이언트가 요청을 하지 않아도 서버에서 css같은 파일을 보낸다.</li>
</ul>
<h3 id="https">HTTPS</h3>
<ul>
<li>HTTPS는 응용계층과 전송계층 사이에 신뢰 계층인 SSL/TLS 계층을 넣어 신뢰할 수 있는 HTTP 요청을 말한다.</li>
<li>SSL/TLS는 보안을 제공하는 프로토콜이다.</li>
<li>공격자가 서버인 척하며 사용자 정보를 가로채는 것을 방지</li>
</ul>
<h3 id="http3">HTTP/3</h3>
<ul>
<li>TCP가 아닌 UDP 기반</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[프록시]]></title>
            <link>https://velog.io/@jun_/%ED%94%84%EB%A1%9D%EC%8B%9C</link>
            <guid>https://velog.io/@jun_/%ED%94%84%EB%A1%9D%EC%8B%9C</guid>
            <pubDate>Wed, 11 May 2022 13:59:54 GMT</pubDate>
            <description><![CDATA[<h1 id="프록시">프록시</h1>
<hr>
<p>프록시 서버란 클라이언트와 서버간의 중계 서버로, 통신을 대리 수행하는 서버다.</p>
<p><img src="https://user-images.githubusercontent.com/72444675/159244622-d704d815-10b7-4b6d-bba1-51ba87c54437.png" alt="다운로드 (14) (1)"></p>
<h2 id="forward-proxy">Forward Proxy</h2>
<p>클라이언트와 인터넷 사이에 위치한다.</p>
<ul>
<li><p>특징</p>
<ol>
<li><p>캐싱</p>
<p>클라이언트 하나가 오늘의 날씨를 요청한다고 해보자. 그럼 프록시는 요청을 받고 인터넷으로 오늘의 날씨를 다시 요청한다.</p>
<p><img src="https://user-images.githubusercontent.com/72444675/159244663-836eb0f9-8a90-48cc-8bdd-dbf1a2094966.png" alt="스크린샷 2021-08-13 오전 3 29 53"></p>
<p>그렇게 받은 요청을 캐시에 저장해두고, 다른 클라이언트가 같은 정보를 요청하면 인터넷에 다시 정보를 요청하지 않고 바로 클라이언트에게 보낸다. 따라서 속도가 매우 향상된다.</p>
<p><img src="https://user-images.githubusercontent.com/72444675/159244702-dc2b80fb-4ab3-4063-96a7-bc57e9e41396.png" alt="스크린샷 2021-08-13 오전 3 30 20"></p>
</li>
<li><p>익명성</p>
<p>클라이언트가 자원을 요청할 때, 프록시를 거친다.</p>
<p>그리고 프록시가 다시 인터넷에 자원을 요청하기 때문에 인터넷에 요청하것은 엄밀히 말하면 클라이언트가 아닌 프록시가 된다.</p>
<p>따라서 어떤 클라이언트가 요청했는지 인터넷을 알지 못한다. (단지 어떤 프록시에서만 요청했는지만 알 수 있음)</p>
<p><img src="https://user-images.githubusercontent.com/72444675/159244745-bdc9b746-c495-430f-af18-bf5a7533f584.png" alt="스크린샷 2021-08-13 오전 3 30 36"></p>
</li>
</ol>
</li>
</ul>
<h2 id="reverse-proxy">Reverse Proxy</h2>
<p>Reverse Proxy는 서버와 인터넷 사이에 존재한다.</p>
<p><img src="https://user-images.githubusercontent.com/72444675/159244789-908dad59-bab6-4d44-9111-b298a0cb0b0f.png" alt="스크린샷 2021-08-13 오전 3 34 05"></p>
<ul>
<li><p>특징</p>
<ol>
<li><p>캐싱</p>
<p>마찬가지로 클라이언트가 요청한 내용을 캐싱한다.</p>
</li>
<li><p>보안</p>
<ul>
<li>서버 정보를 클라이언트에게 숨긴다.</li>
<li>클라이언트는 Reverse Proxy를 실제 서버라고 생각하고 요청을 보내게 된다.</li>
<li>따라서 실제 서버의 IP가 노출되지 않는다.</li>
</ul>
</li>
<li><p>Load Balancing</p>
<p>작업을 나누어 서버의 부하를 분산시킨다.</p>
<p><img src="https://user-images.githubusercontent.com/72444675/159244815-06cc3ffd-3f8c-48db-892e-f824d930fc14.png" alt="스크린샷 2021-08-13 오전 3 35 20"></p>
<p>서버 단일의 성능을 증가시키는 것은 사용자가 많아 질 수록 한계가 발생한다. 따라서 서버의 양을 증가시켜 작업을 나누어 처리할 수 있도록 하는 것이 더 효과적이다.</p>
</li>
</ol>
</li>
</ul>
<p>출처: <a href="https://www.youtube.com/watch?v=YxwYhenZ3BE">https://www.youtube.com/watch?v=YxwYhenZ3BE</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[데이터베이스]]></title>
            <link>https://velog.io/@jun_/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4</link>
            <guid>https://velog.io/@jun_/%EB%8D%B0%EC%9D%B4%ED%84%B0%EB%B2%A0%EC%9D%B4%EC%8A%A4</guid>
            <pubDate>Wed, 11 May 2022 13:59:17 GMT</pubDate>
            <description><![CDATA[<h1 id="1-데이터베이스">1. 데이터베이스</h1>
<h2 id="11-데이터베이스-db">1.1 데이터베이스 (DB)</h2>
<hr>
<p>데이터베이스란 여러 사람들이 공유하고 사용할 목적으로 통합 관리되는 데이터들의 모임이다.</p>
<p>데이터 베이스는 크게 관계형 데이터베이스 (RDB)와 NoSQL로 나뉜다.</p>
<h3 id="관계형-데이터베이스">관계형 데이터베이스</h3>
<p>열과 행을 가진 테이블로 구성되며, 이를 통해 구조적 데이터 관리를 할 수 있다.</p>
<p>SQL을 이용하여 데이터베이스를 조작할 수 있다.</p>
<img src="https://user-images.githubusercontent.com/72444675/159243627-7519f43d-478d-4cf5-9dd7-6184aaed5476.png" width="700px">

<h3 id="nosql">NoSQL</h3>
<p>NoSQL은 Not Only SQL의 약자로 관계형 데이터베이스가 아닌 다른 형태의 데이터 저장 기술을 의미한다.</p>
<p>관계형 데이터베이스의 한계를 극복하기 위한 새로운 형태로 수평적 확장성을 가지고 있다. 문서, 그래프, 키 값, 인 메모리, 검색을 포함해 다양한 데이터 모델을 사용한다.</p>
<h3 id="rdb-vs-nosql">RDB vs NoSQL</h3>
<p>RDB가 클라이언트 / 서버에 맞는 데이터 저장 기술이라면, NoSQL은 클라우드 환경에 맞는 저장 기술이다.</p>
<h2 id="12-데이터베이스-관리-시스템-dbms">1.2 데이터베이스 관리 시스템 (DBMS)</h2>
<hr>
<p>데이터베이스 관리 시스템이란 다수의 사용자들이 데이터베이스 내의 데이터를 접근할 수 있도록 해주는 소프트웨어를 의미한다.</p>
<p>데이터베이스 관리 시스템이 존재하기 이전에는 파일 시스템을 이용하여 데이터를 관리하였고, 이는 데이터 중복, 데이터 불일치의 문제가 있고 만약 독립된 두 공간에서 같은 파일 시스템을 사용해야한다면 서로 공유하기 힘든 문제가 생긴다.</p>
<p>따라서 이러한 문제를 해결하기위해 데이터베이스가 개발되었다.</p>
<h2 id="13-데이터베이스-관리-시스템의-특징">1.3 데이터베이스 관리 시스템의 특징</h2>
<hr>
<ol>
<li><p>데이터의 독립성</p>
<ul>
<li>데이터베이스의 규모(사이즈 등)를 수정해도 관련 응용 프로그램을 수정할 필요가 없다.</li>
</ul>
</li>
<li><p>데이터의 무결성</p>
<ul>
<li>데이터의 유효성을 검사해 데이터의 무결성을 보장한다.</li>
<li>예를들어 입력 조건에 맞지 않는 값은 저장할 수 없도록 방지</li>
</ul>
</li>
<li><p>데이터의 보안성</p>
<ul>
<li>허가된 사용자만 데이터베이스에 접근할 수 있다.</li>
</ul>
</li>
<li><p>데이터의 일관성</p>
<ul>
<li>연관된 정보를 논리적인 구조로 관리하여, 어떤 하나의 데이터를 수정할 때 발생할 수 있는 데이터 불일치를 방지한다.</li>
</ul>
</li>
<li><p>데이터의 중복 최소화</p>
<ul>
<li>데이터베이스는 데이터를 통합해서 관리하여 중복 문제를 해결할 수 있다.</li>
</ul>
</li>
</ol>
<h1 id="2-sql">2. SQL</h1>
<hr>
<p>SQL은 기능에 따라 세 분류로 나눌 수 있다.</p>
<ul>
<li>DDL (데이터 정의 언어)</li>
<li>DML (데이터 조작 언어)</li>
<li>DCL (데이터 제어 언어)</li>
</ul>
<h2 id="21-ddl">2.1 DDL</h2>
<hr>
<h3 id="create">CREATE</h3>
<p>DB와 테이블을 생성할 수 있는 명령어이다.</p>
<pre><code class="language-jsx">CREATE DATABASE [DB_NAME];
// ex) CREATE DATABASE test_db;

CREATE TABLE [TABLE_NAME] ([COLUMN_NAME][DATA_TYPE]);
// ex) CREATE TABLE test_tb (name char(20), age int);</code></pre>
<p>데이터 타입에는 다양한 종류가 있다.</p>
<p>미션에서는 INT형과 CHAR형만 고려하였지만 실제로는 몇십가지가 존재한다.</p>
<p>MySQL 기준 데이터 타입의 종류를 아래 사이트에서 참고해보자.</p>
<p><a href="https://dev.mysql.com/doc/refman/8.0/en/data-types.html">https://dev.mysql.com/doc/refman/8.0/en/data-types.html</a></p>
<p><a href="http://www.incodom.kr/DB_-_%EB%8D%B0%EC%9D%B4%ED%84%B0_%ED%83%80%EC%9E%85/MYSQL">http://www.incodom.kr/DB<em>-</em>데이터_타입/MYSQL</a></p>
<h3 id="drop">DROP</h3>
<p>DROP은 DB와 테이블을 제거하는 명령어이다.</p>
<pre><code class="language-jsx">DROP DATABASE [NAME];
DROP TABLE [NAME];
// ex) DROP TABLE test_tb;</code></pre>
<h2 id="22-dml">2.2 DML</h2>
<hr>
<h3 id="select">SELECT</h3>
<p>SELECT는 데이터를 조회하는 기능을 한다.</p>
<pre><code class="language-jsx">SELECT [COLUMN1] FROM [TABLE] WHERE [CONDITION];
// ex) SELECT name, age FROM test_tb WHERE age = 20;</code></pre>
<p>WHERE을 통해 조건을 설정할 수 있다.</p>
<p>예를 들어 위의 경우는 age 컬럼의 값이 20인 레코드의 name과 age가 조회된다</p>
<p>SELECT * 을 사용하면 name, age뿐만 아니라 모든 속성이 다 보여진다.</p>
<p>조건을 설정하지 않는 경우 모든 레코드가 조회된다.</p>
<h3 id="insert">INSERT</h3>
<p>INSERT는 데이터를 삽입하는 명령어이다.</p>
<pre><code class="language-jsx">INSERT INTO [TABLE_NAME]([COLUMN1]..) VALUES([DATA1],[DATA2]..);
// ex) INSERT INTO [test_tb](name, age) VALUES(&#39;Hong Kil-Dong&#39;,20);</code></pre>
<p>문자열을 삽입할 때는 &#39; &#39; 사이에 값을 넣어야 하고, column과 value 개수가 동일해야 한다.</p>
<p>만약 value 값을 모두 설정하고 싶지 않다면, DEFAULT를 이용할 수 있다.</p>
<p>이 때 테이블에서 설정해둔 기본 값이 지정된다.</p>
<pre><code class="language-jsx">INSERT INTO 테이블명(열1, 열2) VALUES (2, DEFAULT);
//명시적으로 Default 값으로 넣기</code></pre>
<h3 id="delete">DELETE</h3>
<p>조건에 맞는 레코드를 삭제한다.</p>
<pre><code class="language-jsx">DELETE FROM [TABLE] WHERE [CONDITION];
// ex) DELETE FROM test_table WHERE age = 20;</code></pre>
<p>만약 모든 행에 대하여 삭제하고 싶다면 아래 기능을 사용할 수 있다.</p>
<p>DELETE로 하나씩 지우는 것 보다 훨씬 빠르다.</p>
<pre><code class="language-jsx">TRUNCATE TABLE 테이블명;</code></pre>
<h2 id="23-dcl">2.3 DCL</h2>
<hr>
<h3 id="grant">GRANT</h3>
<p>권한 부여</p>
<p>Grant 명령어는 사용자(User)에게 접속권한, 오브젝트 생성권한, DBA 권한 등을 부여할 수 있는 명령어다.</p>
<h3 id="revote">REVOTE</h3>
<p>권환 회수</p>
<p>Revoke 명령어는 사용자(User)에게 부여한 권한을 다시 회수하는 명령어다</p>
<h2 id="24-기타">2.4 기타</h2>
<hr>
<h3 id="where">WHERE</h3>
<p>WRERE은 여러 조건을 조합할 수 있다.</p>
<pre><code class="language-jsx">SELECT * FROM 테이블명 WHERE 조건1 AND 조건2;
SELECT * FROM 테이블명 WHERE 조건1 OR 조건2;
SELECT * FROM 테이블명 WHERE NOT 조건;</code></pre>
<p>프로그래밍 언어처럼 and, or, not을 지원한다.</p>
<h3 id="패턴-매칭">패턴 매칭</h3>
<p>WHERE문에서 = 연산자로 조건을 설정하지 않고 패턴을 이용하여 검색할 수 있다.</p>
<pre><code class="language-jsx">SELECT * FROM 테이블명 WHERE text LIKE &#39;SQL%&#39;;
// text라는 컬럼에서 SQL로 시작하는 레코드를 조회한다. (전방)

SELECT * FROM 테이블명 WHERE text LIKE &#39;%SQL%&#39;;
// text라는 컬럼에서 SQL을 포함하는 내용이 있다면 검색한다. (중간)</code></pre>
<h3 id="count">COUNT</h3>
<p>SELECT와 조합하여 행 개수를 구할 수 있다.</p>
<pre><code class="language-jsx">SELECT COUNT(*) FROM 테이블명 [WHERE 컬럼=조건];</code></pre>
<p>또한 각각의 컬럼의 개수를 따로 셀 수도 있다.</p>
<pre><code class="language-jsx">SELECT COUNT(컬럼1), COUNT(컬럼2) FROM 테이블명;
//컬럼1과 컬럼2의 갯수를 별도로 센다.
//NULL은 집계함수가 세지 않는다.</code></pre>
<h3 id="sum">SUM</h3>
<p>SUM으로 합계를 구할 수 있다.</p>
<pre><code class="language-jsx">SELECT SUM(컬럼) FROM 테이블명;
//열의 합계를 구하여 출력한다.
//NULL 값은 무시한다.

//ex SELECT SUM(price) FROM Mart
// 마트 테이블의 price 컬럼의 총합을 구함</code></pre>
<h3 id="avg">AVG</h3>
<p>AVG로 평균을 구할 수 있다.</p>
<pre><code class="language-jsx">SELECT AVG(컬럼) FROM 테이블명;
//열의 평균을 구하여 출력한다.
//NULL 값은 무시한다.

//ex SELECT AVG(price) FROM Mart
// 마트 테이블의 price 컬럼의 평균을 구함</code></pre>
<h3 id="min--max">MIN &amp; MAX</h3>
<p>최대 최소 구하기</p>
<pre><code class="language-jsx">SELECT MIN(컬럼) FROM 테이블명;
SELECT MAX(컬럼) FROM 테이블명;</code></pre>
<h3 id="join">JOIN</h3>
<p>여러 테이블들을 합쳐서 필요한 정보를 찾아내는 방법이다.</p>
<p><img src="https://user-images.githubusercontent.com/72444675/159243757-8d9c5529-54b8-4cee-838a-5b5ea0d96801.jpeg" alt="cfeb8897ab6e251f3f7fa2f82dc6a6ef (1)"></p>
<img src="https://user-images.githubusercontent.com/72444675/159243821-b2aa52dc-eb3b-435a-8257-431db156564c.png" width="700px">

<h3 id="left-join"><strong>LEFT JOIN</strong></h3>
<p>: 왼쪽 데이터를 기준으로 붙이기(join)</p>
<pre><code>SELECT *
FROM 기준테이블
LEFTJOIN 붙일테이블ON join_기준열</code></pre><br />

<p><img src="https://user-images.githubusercontent.com/72444675/159243974-52f3a912-0d93-4d44-aade-03945e57d131.png" alt="스크린샷_2021-08-12_오전_2 07 13"></p>
<h3 id="right-join"><strong>RIGHT JOIN</strong></h3>
<ul>
<li>오른쪽 데이터를 기준으로 붙이기(join)</li>
</ul>
<pre><code>SELECT *
FROM 기준테이블
RIGHTJOIN 붙일테이블ON join_기준열</code></pre><br />

<p><img src="https://user-images.githubusercontent.com/72444675/159244025-6cab1055-a031-419b-8d10-0dc82a4b0e43.png" alt="스크린샷_2021-08-12_오전_2 07 23"></p>
<h3 id="inner-join"><strong>INNER JOIN</strong></h3>
<ul>
<li>교집합</li>
<li>JOIN하려는 모든 테이블에 존재하는 데이터</li>
</ul>
<pre><code>SELECT *
FROM 기준테이블
INNERJOIN 붙일테이블ON join_기준열</code></pre><br />

<p><img src="https://user-images.githubusercontent.com/72444675/159244087-b823feaa-38aa-45eb-b4b7-687757938a3b.png" alt="스크린샷_2021-08-12_오전_2 07 30"></p>
<h3 id="outer-join"><strong>OUTER JOIN</strong></h3>
<ul>
<li>데이터가 없는 부분은 null로 가져온다.</li>
<li>FULL OUTER JOIN : 전체 합치기</li>
<li>LEFT OUTER JOIN = LEFT JOIN</li>
<li>RIGHT OUTER JOIN = RIGHT JOIN</li>
</ul>
<pre><code>SELECT *
FROM 기준테이블
OUTERJOIN 붙일테이블ON join_기준열</code></pre><br />

<p><img src="https://user-images.githubusercontent.com/72444675/159244122-8fd95281-c224-4975-a40f-820cefc5af70.png" alt="스크린샷_2021-08-12_오전_2 07 40"></p>
<p><a href="https://mizykk.tistory.com/81">https://mizykk.tistory.com/81</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[URI vs URL vs URN]]></title>
            <link>https://velog.io/@jun_/URI-vs-URL-vs-URN</link>
            <guid>https://velog.io/@jun_/URI-vs-URL-vs-URN</guid>
            <pubDate>Wed, 11 May 2022 13:58:32 GMT</pubDate>
            <description><![CDATA[<h1 id="1-uri-vs-url-vs-urn">1. URI vs URL vs URN</h1>
<hr>
<h2 id="11-uri">1.1 URI</h2>
<blockquote>
<p>통합 자원 식별자는 인터넷에 있는 자원을 나타내는 유일한 주소입니다. URI의 존재는 인터넷에서 요구되는 기본 조건으로서 인터넷 프로토콜에 항상 붙어 다닙니다. URI의 하위 개념으로 URL, URN이 있습니다.</p>
</blockquote>
<p>즉, URI는 URL과 URN을 아우르는 말로 볼 수 있다.</p>
<img src="https://user-images.githubusercontent.com/72444675/159243009-82c7aa0c-71c7-4caa-901c-481d94409e67.png" width="700px">

<p><a href="https://itbellstone.tistory.com/86">https://itbellstone.tistory.com/86</a></p>
<h2 id="12-url">1.2 URL</h2>
<blockquote>
<p>URL은 네트워크상에서 자원이 어디 있는지를 알려주기 위한 규약이다. 흔히 웹 사이트 주소로 알고 있지만, URL은 웹 사이트 주소뿐만 아니라 컴퓨터 네트워크 상의 자원을 모두 나타낼 수 있다.</p>
</blockquote>
<ul>
<li>URL은 네트워크 상에서 자원이 어디 있는지를 알려주기 위한 표준이다.</li>
<li>URL은 다음과 같은 구조를 같는다.</li>
</ul>
<pre><code class="language-jsx">scheme://user:password@domain:port/path?query#fragment</code></pre>
<h3 id="protocol-scheme">Protocol (Scheme)</h3>
<ul>
<li>http, https, ftp와 같은 통신 프로토콜을 가리킨다.</li>
<li>URL의 첫 요소이며, 서버와 클라이언트 간에 어떤 방법으로 통신할지 알려준다.</li>
</ul>
<h3 id="user--password">User &amp; Password</h3>
<ul>
<li>몇몇 scheme들은 유저와 비밀번호를 요청하는 경우가 있다.</li>
<li>자원을 요청하기 위해 필요한 userId와 password를 전달한다.</li>
</ul>
<h3 id="domain">Domain</h3>
<ul>
<li>Domain은 URL에서 웹서버의 위치를 지정한다.</li>
<li>Domain을 통해 DNS에서 매핑된 IP로 자원을 요청한다.</li>
</ul>
<h3 id="port">Port</h3>
<ul>
<li>웹 서버에서 자원을 접근하기 위해 사용하는 관문</li>
<li>네트워크 서비스나 특정 프로세스를 식별하는 논리적 단위</li>
<li>네트워크 상에서는 여러 자원과 통신할텐데 같은 ip에 모든 통신이 들어오면 과부하가 발생한다.<ul>
<li>따라서 port로 논리적 단위를 나누어 서버를 구분짓는다.</li>
</ul>
</li>
<li>http의 경우 80번을 기본 포트로 사용하고, https의 경우는 443을 기본으로 사용한다.</li>
</ul>
<h3 id="path">Path</h3>
<ul>
<li>웹 서버에서 자원이 어디에 위치 하는지 경로를 나타낸다.</li>
<li>초기의 웹은 실제 서버에서 자원의 물리적 위치를 나타냈지만, 현대에는 웹 서버에서 추상화 하여 보여준다.</li>
</ul>
<h3 id="query">Query</h3>
<ul>
<li>클라이언트가 자원을 GET 방식으로 요청할 때, 필요한 데이터를 함께 넘겨 줄 목적으로 사용한다.</li>
</ul>
<h3 id="fragment">Fragment</h3>
<ul>
<li>HTML에는 각각의 태그에 id를 부여할 수 있는데, URL에 프래그먼트를 전달하면 페이지가 해당 id가 있는 태그의 위치로 스크롤이 이동한다.</li>
<li>예를 들어 지금 노션 사이트에서 상단에 목차들을 클릭하면 해당 헤더로 이동하는데, 이 때 프래그먼트가 사용된다.</li>
</ul>
<h2 id="13-urn">1.3 URN</h2>
<blockquote>
<p>URN은 urn:scheme를 사용하는 URI를 위한 역사적인 이름이다. URN은 영속적이고, 위치에 독립적인 자원을 위한 지시자로 사용 RFC 2141 문서에서 정의되었다.</p>
</blockquote>
<ul>
<li><p>URN은 리소스가 더 이상 존재하지 않거나 사용할 수 없게 되어도, urn:scheme을 사용하는 URI는 독립적으로 유지할 수 있다.</p>
</li>
<li><p>따라서 URL에서는 파일의 위치에 따라서 path가 바뀌지만, URN은 어느 위치에 파일이 있어도 URI의 문자열이 바뀌지 않는 특징이 있다.</p>
</li>
<li><p>아래와 같은 형식이 URN이다.</p>
</li>
</ul>
<pre><code class="language-jsx">urn:namespace:the:id:for:file</code></pre>
<h1 id="2-url-encoding">2. URL Encoding</h1>
<hr>
<p>URL로 사용할 수 없는 문자(특수 문자, 예약 문자 등)을 사용하 수 있도록 인코딩 해준다.</p>
<p>인코딩된 문자는 triplet(트리플렛, 세개가 한묶음)들로 인코딩되며 인코딩된 문자는 &#39;%&#39;로 시작하고 그 뒤의 두 숫자는 16진수로 표현된다.</p>
<h3 id="예약-문자">예약 문자</h3>
<p><a href="https://tools.ietf.org/html/rfc3986">RFC3986</a>에 따르면 URI에서 문법적으로 사용되는 예약 문자가 있다.</p>
<p><img src="https://user-images.githubusercontent.com/72444675/159243168-7bae7f51-8aab-440a-9b3f-62cc360f2c49.png" alt="스크린샷_2018-03-18_18 00 07 (1)"></p>
<p><a href="https://ko.wikipedia.org/wiki/%ED%8D%BC%EC%84%BC%ED%8A%B8_%EC%9D%B8%EC%BD%94%EB%94%A9">https://ko.wikipedia.org/wiki/퍼센트_인코딩</a></p>
<p>또한 비 예약 문자도 있는데, 이들은 인코딩을 하지 않는 것을 권장한다.</p>
<p><img src="https://user-images.githubusercontent.com/72444675/159243196-4367af65-2cfb-42c6-bea5-9802031cdd0f.png" alt="스크린샷_2018-03-18_18 08 03 (1)"></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[HTTP 프로토콜]]></title>
            <link>https://velog.io/@jun_/HTTP-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C</link>
            <guid>https://velog.io/@jun_/HTTP-%ED%94%84%EB%A1%9C%ED%86%A0%EC%BD%9C</guid>
            <pubDate>Wed, 11 May 2022 13:58:03 GMT</pubDate>
            <description><![CDATA[<h1 id="1-http">1. HTTP</h1>
<hr>
<p>HTTP (Hyper Text Transfer Protocol)은 www상에서 정보를 주고 받을 수 있는 하나의 프로토콜이다. 주로 HTML을 주고 받는다.</p>
<ul>
<li>프로토콜이란 상호 간에 정의한 규칙을 의미하며 소스와 타켓이 데이터를 주고 받기 위해 정의 되었다.</li>
</ul>
<h2 id="11-http-프로토콜의-특징">1.1 HTTP 프로토콜의 특징</h2>
<hr>
<ul>
<li>HTTP 프로토콜은 상태가 없는 (stateless) 프로토콜이다.</li>
<li>즉, 데이터를 주고 받기 위한 각각의 데이터 요청이 서로 독립적이다.</li>
<li>쉽게 말하면, 이전 데이터 요청과 다음 데이터 요청이 서로 관련이 없다.</li>
<li>HTTP는 일반적으로 TCP 통신을 하며 기본 포트는 80번이다.</li>
</ul>
<h2 id="12-http-요청--응답">1.2 HTTP 요청 &amp; 응답</h2>
<hr>
<p>HTTP 프로토콜은 데이터를 주고 받기 위해 아래와 같이 요청(Request)를 보내고 응답(Response)을 받아야 한다.</p>
<img src="https://user-images.githubusercontent.com/72444675/159242552-0f367133-068c-4e87-bb0f-30eb913de8ed.png" width="700px">

<p><a href="https://joshua1988.github.io/web-development/http-part1/">https://joshua1988.github.io/web-development/http-part1/</a></p>
<ul>
<li>여기서 클라이언트(브라우저)는 서버에 요청을 보내고, 서버는 클라이언트에게 요청에 대한 응답을 준다.</li>
<li>예를 들어 브라우저 주소창에 <a href="http://www.naver.com%EC%9D%84">www.naver.com을</a> 입력하면 네이버 서버에 html문서를 요청(Request)한 것이고, 네이버 서버는 내 브라우저에게 응답(Response)을 보내 html문서가 전송된다.</li>
</ul>
<h3 id="url-구조">URL 구조</h3>
<p><img src="https://user-images.githubusercontent.com/72444675/159242610-d8484ca1-4d3b-4c42-95b3-c7a0ae1b79df.png" alt="url-structure (1)"></p>
<ul>
<li>url에 resource path를 넣어 특정 데이터를 요청할 수 있다.</li>
<li>또한 쿼리문을 넣어 데이터베이스로 부터 데이터를 가져오기도 한다.</li>
</ul>
<h2 id="13-http-메서드">1.3 HTTP 메서드</h2>
<hr>
<p>앞서 말했듯이 url을 이용해 서버에게 특정 데이터를 요청한다.</p>
<p>여기서 요청하는 데이터에 특정 동작을 수행하고 싶으면 HTTP 요청 메서드를 이용한다.</p>
<ul>
<li><strong>GET</strong> : 존재하는 자원에 대한 <strong>요청</strong></li>
<li><strong>POST</strong> : 새로운 자원을 <strong>생성</strong></li>
<li><strong>PUT</strong> : 존재하는 자원에 대한 <strong>변경</strong></li>
<li><strong>DELETE</strong> : 존재하는 자원에 대한 <strong>삭제</strong></li>
</ul>
<p>예를 들어 페이스북에서 게시물을 보려면 get요청을 통해 데이터를 불러와 볼 수 있고, 게시물을 올리려면 post요청을 통해 새로운 데이터를 등록할 수 있다.</p>
<h2 id="14-http-상태-코드">1.4 HTTP 상태 코드</h2>
<hr>
<p>클라이언트가 서버에게 요청(Response)을 보내면 서버는 상태에 따라 응답을 보내 준다. 이 때 여러 상태에 따른 다양한 코드들이 존재한다.</p>
<h3 id="2xx---성공">2xx - 성공</h3>
<p>200번대의 상태 코드는 대부분 성공을 의미한다.</p>
<ul>
<li>200: GET 요청에 대한 성공</li>
<li>204: 성공했으나 응답 본문에 데이터가 없음 (No Content)</li>
<li>205: 성공했으나 클라이언트의 화면을 새로 고침하도록 권고 (Reset Content)</li>
<li>206: 성공했으나 일부 범위의 데이터만 반환 (Partial Content)</li>
</ul>
<h3 id="3xx---리다이렉션">3xx - 리다이렉션</h3>
<p>300번대의 상태 코드는 대부분 클라이언트가 이전 주소로 데이터를 요청하여 서버에서 새 URL로 리다이렉트를 유도하는 경우이다.</p>
<ul>
<li>301 : 요청한 자원이 새 URL에 존재 (Moved Permanently)</li>
<li>303 : 요청한 자원이 임시 주소에 존재 (See Othe)</li>
<li>302: 하이퍼텍스트 전송 프로토콜 (HTTP)의 302 Found 리다이렉트 상태 응답 코드는 클라이언트가 요청한 리소스가 Location (en-US) 헤더에 주어진 URL에 일시적으로 이동되었음을 가리킨다.</li>
<li>304 : 요청한 자원이 변경되지 않았으므로 클라이언트에서 캐싱된 자원을 사용하도록 권고. <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/ETag">ETag</a>와 같은 정보를 활용하여 변경 여부를 확인 (Not Modified)</li>
</ul>
<h3 id="4xx---클라이언트-에러">4xx - 클라이언트 에러</h3>
<p>400번대 상태 코드는 대부분 클라이언트의 코드가 잘못된 경우이다.</p>
<p>유효하지 않은 자원을 요청 했거나, 요청이나 권한이 잘못된 경우 발생한다.</p>
<p>가장 익숙한 상태 코드는 404 코드 이며, 요청한 자원이 서버에 없다는 의미이다.</p>
<ul>
<li><p>url을 잘못 입력했거나</p>
</li>
<li><p>url에 들어간 resouce path가 유효하지 않거나</p>
</li>
<li><p>쿼리문이 잘못 됐거나 등</p>
</li>
<li><p>400 : 잘못된 요청 (Bad Request)</p>
</li>
<li><p>401 : 권한 없이 요청. Authorization 헤더가 잘못된 경우 (Unauthorized)</p>
</li>
<li><p>403 : 서버에서 해당 자원에 대해 접근 금지 (Forbidden)</p>
</li>
<li><p>405 : 허용되지 않은 요청 메서드 (Method Not Allowed)</p>
</li>
<li><p>409 : 최신 자원이 아닌데 업데이트하는 경우. (Conflict)</p>
</li>
</ul>
<h2 id="15-request--response">1.5 Request &amp; Response</h2>
<hr>
<h3 id="http-request">HTTP Request</h3>
<ul>
<li><p>Method</p>
<ul>
<li>GET: 리소스를 요청하고 반환 받기 위해 사용되는 메소드이다.</li>
<li>HEAD: 메시지 헤더 정보를 얻는다.</li>
<li>POST: 요청된 자원을 생성하기 위해 사용된다.</li>
<li>PUT: 요청된 자원을 수정하기 위해 사용한다.</li>
<li>PATCH: PUT과 다르게 자원의 일부만 수정한다.</li>
<li>DELETE: 요청한 자원을 삭제한다.</li>
<li>TRACE: 루프백 메시지를 호출하기 위한 테스트용</li>
<li>OPTION: 웹 서버에서 지원하는 메소드를 알아본다.</li>
<li>CONNECT: 프락시 기능을 요청할 때 사용한다.</li>
</ul>
</li>
<li><p>Header</p>
</li>
</ul>
<pre><code class="language-jsx">GET /home.html HTTP/1.1
Host: developer.mozilla.org
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/ *;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: https://developer.mozilla.org/testpage.html
Connection: keep-alive
Upgrade-Insecure-Requests: 1
If-Modified-Since: Mon, 18 Jul 2016 02:36:04 GMT
If-None-Match: &quot;c561c68d0ba92bbeb8b0fff2a9199f722e3a621a&quot;
Cache-Control: max-age=0
https://gmlwjd9405.github.io/2019/01/28/http-header-types.html</code></pre>
<ul>
<li>Host
요청하는 호스트에 대한 호스트명 및 포트번호 (필수 항목)</li>
<li>User-Agent
클라이언트 소프트웨어(브라우저, OS) 명칭 및 버전 정보</li>
<li>Cookie
서버에 의해 Set-Cookie로 클라이언트에게 설정된 쿠키 정보
(쿠키/캐시 관련)</li>
<li>Referer
바로 직전에 머물었던 웹 링크 주소</li>
<li>If-Modified-Since
제시한 일시 이후로만 변경된 리소스를 취득 요청</li>
<li>Authorization
인증 토큰(JWT/Bearer 토큰)을 서버로 보낼 때 사용하는 헤더
“토큰의 종류(Basic, Bearer 등) + 실제 토큰 문자”를 전송</li>
<li>Origin
서버로 POST 요청을 보낼 때, 요청이 어느 주소에서 시작되었는지 나타낸다.
여기서 요청을 보낸 주소와 받는 주소가 다르면 CORS 에러가 발생한다.
응답 헤더의 Access-Control-Allow-Origin와 관련
다음 4개는 주로 HTTP 메세지 Body의 속성 또는 내용 협상용 항목들</li>
<li>Accept
클라이언트 자신이 원하는 미디어 타입 및 우선순위를 알린다.
텍스트(text/html,text/plain,…),이미지(image/jpeg,…) 등
Ex) Accept: <em>/</em>
어떤 미디어 타입도 가능하다.
Ex) Accept: image/*
모든 이미지 유형이 가능하다.</li>
<li>Accept-Charset
클라이언트 자신이 원하는 문자 집합</li>
<li>Accept-Encoding
클라이언트 자신이 원하는 문자 인코딩 방식</li>
<li>Accept-Language
클라이언트 자신이 원하는 가능한 언어
각각이 HTTP Entity Header 항목 중에 Content-Type, Content-Type charset-xxx, Content-Encoding, Content-Language과 일대일로 대응된다.</li>
</ul>
<h3 id="http-response">HTTP Response</h3>
<pre><code class="language-jsx">200 OK
Access-Control-Allow-Origin: *
Connection: Keep-Alive
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
Date: Mon, 18 Jul 2016 16:06:00 GMT
Etag: &quot;c561c68d0ba92bbeb8b0f612a9199f722e3a621a&quot;
Keep-Alive: timeout=5, max=997
Last-Modified: Mon, 18 Jul 2016 02:36:04 GMT
Server: Apache
Set-Cookie: mykey=myvalue; expires=Mon, 17-Jul-2017 16:06:00 GMT; Max-Age=31449600; Path=/; secure
Transfer-Encoding: chunked
Vary: Cookie, Accept-Encoding
X-Backend-Server: developer2.webapp.scl3.mozilla.com
X-Cache-Info: not cacheable; meta data too large
X-kuma-revision: 1085259
x-frame-options: DENY
https://gmlwjd9405.github.io/2019/01/28/http-header-types.html</code></pre>
<ul>
<li>Header<ul>
<li>Server
서버 소프트웨어 정보</li>
<li>Set-Cookie
서버측에서 클라이언트에게 세션 쿠키 정보를 설정 (RFC 2965에서 규정)</li>
<li>Expires
리소스가 지정된 일시까지 캐시로써 유효함을 나타낸다. 즉, 응답 컨텐츠가 언제 만료되는지를 나타낸다.
Ex) Expires: Thu, 26 Jul 2018 07:28:00 GMT
Cache-Control과 별개로 응답에 Expires라는 헤더를 줄 수 있다.
단, Cache-Control의 max-age가 있는 경우 이 헤더는 무시</li>
<li>Age
캐시 응답. max-age 시간 내에서 얼마나 흘렀는지 초 단위로 알려준다.</li>
<li>ETag
HTTP 컨텐츠가 바뀌었는지를 검사할 수 있는 태그</li>
<li>Allow
해당 엔터티에 대해 서버 측에서 지원 가능한 HTTP 메소드의 리스트를 나타낸다. 때론, HTTP 요청 메세지의 HTTP 메소드 OPTIONS에 대한 응답용 항목으로 사용된다. - OPTIONS: 웹서버측 제공 HTTP 메소드에 대한 질의 - Ex) Allow: GET,HEAD
405 Method Not Allowed 에러와 함께
웹 서버에서 제공 가능한 HTTP 메서드는 GET, HEAD 뿐임을 알린다.</li>
<li>Access-Control-Allow-Origin
요청을 보내는 프론트 주소와 받는 백엔드 주소가 다르면 CORS 에러가 발생 서버에서 이 헤더에 프론트 주소를 적어주어야 에러가 나지 않는다. - Ex) Access-Control-Allow-Origin: <a href="http://www.boostcamp">www.boostcamp</a>
프로토콜, 서브도메인, 도메인, 포트 중 하나만 달라도 CORS 에러가 난다. - Ex) Access-Control-Allow-Origin: *
만약 주소를 일일이 지정하기 싫다면 *으로 모든 주소에 CORS 요청을 허용되지만 그만큼 보안이 취약해진다.</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[TCP vs UDP]]></title>
            <link>https://velog.io/@jun_/TCP-vs-UDP</link>
            <guid>https://velog.io/@jun_/TCP-vs-UDP</guid>
            <pubDate>Wed, 11 May 2022 13:57:09 GMT</pubDate>
            <description><![CDATA[<h1 id="tcp-vs-udp">TCP VS UDP</h1>
<hr>
<p>두 프로토콜은 모두 패킷을 한 PC에서 다른 PC로 전달해주는 IP프로토콜을 기반으로 구현되어 있지만, 서로 다른 특징을 가지고 있습니다.</p>
<ul>
<li>TCP 송신 과정</li>
</ul>
<p><img src="https://user-images.githubusercontent.com/72444675/159241184-552e68ec-b601-4c4f-b249-4dc6d43139f5.png" alt="스크린샷 2022-03-21 오후 7 12 25"></p>
<ul>
<li>UDP 송신 과정</li>
</ul>
<p><img src="https://user-images.githubusercontent.com/72444675/159241300-09cdba76-29d9-4922-a3e5-6af9a799d71b.png" alt="스크린샷 2022-03-21 오후 7 13 12"></p>
<ul>
<li>TCP에 반해 UDP는 일방적으로 전송한다.</li>
<li>따라서 이는 신뢰성있는 통신을 보장하지 않는다.</li>
<li>즉, 신뢰성이 요구되는 애플리케이션은 TCP를 사용하고, 간단한 데이터를 빠른 속도로 전송하고자 하는 애플리케이션은 UDP를 사용한다.</li>
</ul>
<h2 id="tcp의-특징">TCP의 특징</h2>
<hr>
<ul>
<li><p>연결형 서비스</p>
<ul>
<li>3-way handshaking 과정을 통해 연결을 설정</li>
<li>4-way handshaking 을 통해 연결을 해제.</li>
</ul>
</li>
<li><p>흐름 제어</p>
<ul>
<li>데이터 처리 속도를 조절하여 수신자의 오버플로우를 방지</li>
<li>송신하는 곳에서 감당이 안되게 많은 데이터를 빠르게 보내 수신하는 곳에서 문제가 일어나는 것을 막는다.</li>
<li>수신자가 <code>윈도우크기(Window Size)</code> 값을 통해 수신량을 정할 수 있다.</li>
</ul>
</li>
<li><p>혼잡 제어</p>
<ul>
<li>네트워크 내의 패킷 수가 넘치게 증가하지 않도록 방지</li>
<li>정보의 소통량이 과다하면 패킷을 조금만 전송하여 혼잡 붕괴 현상이 일어나는 것을 막는다.</li>
</ul>
</li>
<li><p>신뢰성이 높은 전송</p>
<ul>
<li>ACK가 중복으로 올 경우 패키 이상을 감지하고 재전송을 요청한다.</li>
<li>일정 시간동안 ACK를 받지 못할 경우 재전송을 요청한다.</li>
</ul>
</li>
<li><p>전이중, 점대점 방식</p>
<ul>
<li>전송이 양방향이다.</li>
<li>각 연결이 2개의 종단점을 같는다.</li>
</ul>
</li>
</ul>
<h2 id="tcp-header-정보">TCP Header 정보</h2>
<hr>
<p><img src="https://user-images.githubusercontent.com/72444675/159241347-13ad430f-de74-4248-b88b-54663abbe520.png" alt="1_(4)"></p>
<p><a href="https://www.notion.so/951e3f7fadd34acf8e6730c8e53d5081">TCP Header 정보</a></p>
<table>
<thead>
<tr>
<th>필드</th>
<th>내용</th>
<th>크기</th>
</tr>
</thead>
<tbody><tr>
<td>송수신자의 포트 번호</td>
<td>TCP로 연결되는 가상 회선 양단의 송수신 프로세스에 할당되는 포트 주소</td>
<td>16</td>
</tr>
<tr>
<td>시퀀스 번호(Sequence Number)</td>
<td>송신자가 지정하는 순서 번호, 전송되는 바이트 수를 기준으로 증가.SYN = 1 : 초기 시퀀스 번호가 된다. ACK 번호는 이 값에 1을 더한 값.SYN = 0 : 현재 세션의 이 세그먼트 데이터의 최초 바이트 값의 누적 시퀀스 번호</td>
<td>32</td>
</tr>
<tr>
<td>응답 번호(ACK Number)</td>
<td>수신 프로세스가 제대로 수신한 바이트의 수를 응답하기 위해 사용.</td>
<td>32</td>
</tr>
<tr>
<td>데이터 오프셋(Data Offset)</td>
<td>TCP 세그먼트의 시작 위치를 기준으로 데이터의 시작 위치를 표현(TCP 헤더의 크기)</td>
<td>4</td>
</tr>
<tr>
<td>예약 필드(Reserved)</td>
<td>사용을 하지 않지만 나중을 위한 예약 필드이며 0으로 채워져야한다.</td>
<td>6</td>
</tr>
<tr>
<td>제어 비트(Flag Bit)</td>
<td>SYN, ACK, FIN 등의 제어 번호 -&gt; 아래 추가 설명 참조</td>
<td>6</td>
</tr>
<tr>
<td>윈도우 크기(Window)</td>
<td>수신 윈도우의 버퍼 크기를 지정할 때 사용. 0이면 송신 프로세스의 전송 중지</td>
<td>16</td>
</tr>
<tr>
<td>체크섬(Checksum)</td>
<td>TCP 세그먼트에 포함되는 프로토콜 헤더와 데이터에 대한 오류 검출 용도</td>
<td>16</td>
</tr>
<tr>
<td>긴급 위치(Urgent Pointer)</td>
<td>긴급 데이터를 처리하기 위함, URG 플래그 비트가 지정된 경우에만 유효</td>
<td>16</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[OSI 7계층]]></title>
            <link>https://velog.io/@jun_/OSI-7%EA%B3%84%EC%B8%B5</link>
            <guid>https://velog.io/@jun_/OSI-7%EA%B3%84%EC%B8%B5</guid>
            <pubDate>Wed, 11 May 2022 13:56:24 GMT</pubDate>
            <description><![CDATA[<h1 id="1-osi-7계층">1. OSI 7계층</h1>
<hr>
<blockquote>
<p>OSI 7계층이란 국제 표준화 기구 ISO에서 개발한 모델로서, 네트워크 프로토콜 디자인과 통신을 계층으로 나눠 설명한 것입니다.</p>
</blockquote>
<p><img src="https://user-images.githubusercontent.com/72444675/159240035-17606636-2f7c-4e7b-a436-367a0031748b.png" alt="스크린샷_2021-08-05_오전_2 06 58"></p>
<h2 id="11-응용-계층-application">1.1 응용 계층 (Application)</h2>
<hr>
<ul>
<li>OSI의 최상위 7계층인 응용계층은 사용자 또는 어플리케이션이 네트워크에 접근할 수 있도록 해줍니다.</li>
<li>사용자를 위한 인터페이스를 지원합니다.</li>
<li>즉, 사용자에게 보이는 유일한 계층이고 메일 전송, 인터넷 접속 등을 예로 들 수 있습니다.</li>
<li>또한 HTTP, FTP 등의 프로토콜이 응용 계층에 속합니다.</li>
</ul>
<h2 id="12-표현-계층-presentation">1.2 표현 계층 (Presentation)</h2>
<hr>
<ul>
<li>표현계층은 응용계층으로부터 전달받거나 전송하는 데이터의 인코딩 및 디코딩이 이루어지는 계층입니다.</li>
<li>주로 base64 방식으로 인코딩이 진행됩니다.</li>
</ul>
<h3 id="base64">Base64</h3>
<ul>
<li><p>Base64는 Binary Data를 텍스트로 변경하는 인코딩 방식 중 하나로, 바이너리 데이터를 문자 코드에 영향을 받지 않는 공통 64개의 ASCII 영역의 문자들로 이루어진 문자열로 변경합니다.</p>
</li>
<li><p>Base64 인코딩 과정
먼저 24bit의 buffer를 생성하여 위쪽(MSB)부터 바이트 데이터를 넣은 뒤,
버퍼의 위쪽부터 <strong>6bit 단위로 잘라</strong> Base64 테이블의 <strong>ASCII 문자로 변환</strong>합니다.
<img src="https://user-images.githubusercontent.com/72444675/159240082-eb7ba1a8-ceda-40c5-baec-a9d864525ea5.png" alt="99C7F8345C90BF422C"></p>
<p>ex) Man → TWFu
<img src="https://user-images.githubusercontent.com/72444675/159240118-fbfb1a7f-ba9e-4a07-bf1e-c5611015a3a0.png" alt="993070395C90BE292A"></p>
</li>
</ul>
<p>만약 24bits의 버퍼가 모두 채워지지 않을 경우 다음과 같이 처리합니다.</p>
<p><img src="https://user-images.githubusercontent.com/72444675/159240157-c637248e-570a-4927-9771-1426fe05b458.png" alt="99B430495C91CF022A"></p>
<p>6비트 까지는 0으로 채우고 뒤의 나머지는 &#39;=&#39;문자로 채웁니다.</p>
<h2 id="13-세션-계층-session">1.3 세션 계층 (Session)</h2>
<hr>
<ul>
<li>세션 계층은 네트워크상 양쪽 연결을 관리하고 연결을 지속시켜주는 계층입니다.</li>
<li>세션을 만들고 유지하며, 세션 종료, 전송 중단 시 복구 기능이 있습니다.</li>
<li>이 계층은 TCP/IP 세션을 만들고 없애는 책임을 집니다.</li>
<li>전이중, 반이중, 단방향 통신을 결정합니다.</li>
</ul>
<h2 id="14-전송-계층-transport-layer">1.4 전송 계층 (Transport Layer)</h2>
<hr>
<ul>
<li>전송 계층은 양 끝단 사용자들이 데이터를 주고 받을 수 있게 하는 계층입니다.</li>
<li>대표적으로 TCP, UDP 프로토콜이 있습니다.</li>
</ul>
<h3 id="tcp">TCP</h3>
<p>TCP의 경우 신뢰성있는 통신을 보장합니다. 따라서 데이터가 전달되는 과정에서 여러 스위치 라우터 등등을 거치면서 데이터가 잘못 전달되는 현상이나 전달 안 되는 경우에 오류 제어, 흐름 제어를 수행해 신뢰성있는 데이터가 전달될 수 있도록 합니다.</p>
<p>TCP는 처음 연결할 때 3-way handshake 방식으로 목적지와 상호 패킷을 교환하여 연결합니다. 연결을 종료할 때는 4-way handshake 방식을 사용합니다.</p>
<ul>
<li>3-way handshake<ul>
<li>먼저 클라이언트가 서버에 SYN을 보냅니다.</li>
<li>SYN을 받은 서버는 클라이언트에게 받았다는 신호 SYN+ACK를 보냅니다.</li>
<li>클라이언트는 그 신호 마저 받았다고 서버에게 ACK로 알립니다.</li>
</ul>
</li>
</ul>
<img src="https://user-images.githubusercontent.com/72444675/159240526-aae75586-df15-4fc3-b946-e0d507f0a6cd.png" width="700px">

<ul>
<li>4-way handshake<ul>
<li>클라이언트는 연결을 종료하기 위해 FIN을 전송합니다.</li>
<li>FIN을 받은 서버는 확인 ACK를 보냅니다.</li>
<li>서버의 ACK를 받은 클라이언트는 FIN이 오기 까지 기다립니다.</li>
<li>서버가 FIN을 보내면 비로소 클라이언트는 ACK로 잘 받았다고 알립니다.</li>
</ul>
</li>
</ul>
<img src="https://user-images.githubusercontent.com/72444675/159240625-b487fa0f-bacf-4536-9a3f-d1545e531ee5.png" width="700px">

<h2 id="15-네트워크-계층-network-layer">1.5 네트워크 계층 (Network Layer)</h2>
<hr>
<ul>
<li>네트워크 계층은 우리가 흔히 아는 IP 주소를 제공하는 계층입니다.</li>
<li>네트워크 계층에서는 대표적으로 노드들을 거칠때마다 라우팅 해주는 역할을 담당합니다.</li>
<li>이 계층의 대표적인 장비는 라우터, L3 스위치, IP공유기 등 입니다.</li>
<li>전송 단위는 packet입니다.</li>
</ul>
<h2 id="16-데이터-링크-계층-data-link-layer">1.6 데이터 링크 계층 (Data Link Layer)</h2>
<hr>
<ul>
<li>데이터 링크 계층은 Point to Point 간의 신뢰성 있는 전송을 보장하기 위한 계층입니다.</li>
<li>물리주소인 MAC주소를 이용하여 전송을 보장합니다.</li>
<li>전송 단위는 Frame입니다.</li>
</ul>
<h2 id="17-물리-계층-physical-layer">1.7 물리 계층 (Physical Layer)</h2>
<hr>
<ul>
<li>물리 계층은 말 그대로 하드웨어 전송 기술로 이루어져 있습니다.</li>
<li>전기적인, 기계적인 신호를 주고 받는 역할을 합니다.</li>
<li>0, 1 비트 단위로 통신을 합니다.</li>
<li>통신 케이블, 허브, 리피터 등이 해당됩니다.</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>