<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>I'm_Hazel</title>
        <link>https://velog.io/</link>
        <description>개발자를 꿈꾸며 하루하루 고군분투 중!</description>
        <lastBuildDate>Wed, 25 Oct 2023 07:50:35 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>I'm_Hazel</title>
            <url>https://velog.velcdn.com/images/hazel_1130/profile/feeb0098-b0c3-452b-bd5e-5fbd67899e8f/image.jpg</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. I'm_Hazel. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/hazel_1130" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Flutter 찍먹하기 시리즈 - 2]]></title>
            <link>https://velog.io/@hazel_1130/Flutter-%EC%B0%8D%EB%A8%B9%ED%95%98%EA%B8%B0-%EC%8B%9C%EB%A6%AC%EC%A6%88-Dart-2</link>
            <guid>https://velog.io/@hazel_1130/Flutter-%EC%B0%8D%EB%A8%B9%ED%95%98%EA%B8%B0-%EC%8B%9C%EB%A6%AC%EC%A6%88-Dart-2</guid>
            <pubDate>Wed, 25 Oct 2023 07:50:35 GMT</pubDate>
            <description><![CDATA[<p>이 글은 골든래빗 《코드팩토리의 플러터 프로그래밍》의 2장 객체지향프로그래밍의 써머리입니다</p>
<h1 id="스태틱">스태틱</h1>
<ul>
<li><code>static</code>을 사용할 시 모든 속성은 클래스 자체에 귀속된다.</li>
</ul>
<pre><code class="language-dart">class Counter{
    static int i=0; // static 키워드를 이용한 변수 선언

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

void main() {
Counter count1 = Counter();
Counter count2 = Counter();
Counter count3 = Counter();
]
</code></pre>
<ul>
<li>생성자에서 <code>static</code> 값을 지정하지 못하며 인스턴스끼리 공유해야하는 정보에 지정해야한다.</li>
</ul>
<h3 id="정적-메서드">정적 메서드</h3>
<ul>
<li><code>static</code> 키워드를 사용하여 클레스의 메서드를 정적으로 선언 가능</li>
<li>해당 정적 메서드는 클래스의 이름으로 호출되며 클래스의 인스턴스를 생성하지 않고도 사용 가능하다</li>
</ul>
<pre><code class="language-dart">class MathUtils {
  static int add(int a, int b) {
    return a + b;
  }
}

void main() {
  int result = MathUtils.add(3, 5);
  print(result); // 8
}</code></pre>
<h3 id="정적-변수">정적 변수</h3>
<ul>
<li>클래스 내부에 <strong><code>static</code></strong> 키워드를 사용하여 정적 변수를 선언</li>
<li>클래스의 모든 인스턴스에서 공유되며 객체를 생성하지 않고도 접근 가능</li>
</ul>
<pre><code class="language-dart">class Counter {
  static int count = 0;

  void increment() {
    count++;
  }
}

void main() {
  Counter c1 = Counter();
  Counter c2 = Counter();

  c1.increment();
  c2.increment();

  print(Counter.count); // 2
}</code></pre>
<h3 id="상수-변수">상수 변수</h3>
<ul>
<li><strong><code>static const</code></strong>를 사용하여 클래스 내에서 상수 변수를 정의</li>
<li>해당 상수는 변경 불가능하며 클래스 인스턴스와 관계없이 동일한 값을 가진다</li>
</ul>
<pre><code class="language-dart">class Constants {
  static const int maxItems = 10;
}

void main() {
  print(Constants.maxItems); // 10
}</code></pre>
<h2 id="캐스케이드-연산자">캐스케이드 연산자</h2>
<ul>
<li>인스턴스에서 해당 인스턴스의 속성 혹은 멤버 함수를 연속해서 사용하는 기호</li>
<li>.. 를 이용해서 사용</li>
</ul>
<pre><code class="language-dart">var paint = Paint()
  ..color = Colors.black
  ..strokeCap = StrokeCap.round
  ..strokeWidth = 5.0;</code></pre>
<ul>
<li>객체의 메서드를 연속적으로 호출하거나 객체의 속성을 연속적으로 설정할 때 편리하게 사용</li>
</ul>
<pre><code class="language-dart">class Circle {
  double radius = 0.0;

  void calculateArea() {
    print(&#39;Area: ${3.14 * radius * radius}&#39;);
  }
}

void main() {
  Circle circle = Circle()
    ..radius = 5.0
    ..calculateArea(); // Area: 78.5
}</code></pre>
<ul>
<li>메서드 호출과 속성 설정을 함께 사용하여 복잡한 작업을 수행 가능하다</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Flutter 찍먹하기 시리즈 -1]]></title>
            <link>https://velog.io/@hazel_1130/Flutter-%EC%B0%8D%EB%A8%B9%ED%95%98%EA%B8%B0-%EC%8B%9C%EB%A6%AC%EC%A6%88-1</link>
            <guid>https://velog.io/@hazel_1130/Flutter-%EC%B0%8D%EB%A8%B9%ED%95%98%EA%B8%B0-%EC%8B%9C%EB%A6%AC%EC%A6%88-1</guid>
            <pubDate>Sat, 14 Oct 2023 14:43:23 GMT</pubDate>
            <description><![CDATA[<p>이 글은 골든래빗 《코드팩토리의 플러터 프로그래밍》의 1장 써머리입니다</p>
<h2 id="dart란">Dart란?</h2>
<p>Dart는 모든 플랫폼에서 빠른 앱을 개발하기 위해 클라이언트에 최적화된 언어다.</p>
<p>Dart의 특징이자 장점은 아래와 같다</p>
<ul>
<li>UI에 최적화되어있다<ul>
<li>null-safety ( <a href="https://dart-ko.dev/null-safety">https://dart-ko.dev/null-safety</a> )</li>
<li>isolate 기반의 동시성 ( <a href="https://dart-ko.dev/language/concurrency">https://dart-ko.dev/language/concurrency</a> )</li>
</ul>
</li>
<li>효율적인 개발환경을 제공해준다
-Hot Reloading ( <a href="https://docs.flutter.dev/tools/hot-reload">https://docs.flutter.dev/tools/hot-reload</a> )</li>
<li>어떤 플랫폼이든 빠른 속도를 자랑한다<ul>
<li>AOT complie ( <a href="https://dart-ko.dev/overview#native-platform">https://dart-ko.dev/overview#native-platform</a> )</li>
</ul>
</li>
</ul>
<h2 id="dart-기초-문법">Dart 기초 문법</h2>
<p><strong>main 함수</strong></p>
<pre><code class="language-dart">void main(){
}</code></pre>
<ul>
<li>( )로 매개변수 지정해 받아오기 가능</li>
</ul>
<p><strong>print( ) 함수</strong></p>
<pre><code class="language-dart">void main(){
pring(&#39;Hello Hazel)
}</code></pre>
<p>-문자열 콘솔에 출력</p>
<p><strong>다양한 변수 타입</strong></p>
<ul>
<li><p>var -[기본형식] var 변수명 = 값;</p>
<ul>
<li>자동 타입추론 기능 제공 -값 변경 가능 , 중복 선언 불가능</li>
</ul>
</li>
<li><p>dynamic</p>
<ul>
<li>변수의 타입을 자유롭게 선언 가능</li>
</ul>
</li>
<li><p>상수 final / const</p>
<ul>
<li>final (런타임 상수) - const (빌드타임 상수)</li>
</ul>
<pre><code class="language-dart">void main(){
final DateTime now = DateTime.now();
const DateTime now = DateTime.now();
}</code></pre>
</li>
<li><p>해당 코드 실행시 const에서 에러가 발생한다 (const는 빌드될 때 값이 확정되기 떄문)</p>
</li>
</ul>
<p><strong>변수 타입</strong></p>
<table>
<thead>
<tr>
<th>변수타입</th>
<th></th>
</tr>
</thead>
<tbody><tr>
<td>string : 문자열</td>
<td>int : 정수</td>
</tr>
<tr>
<td>double : 실수</td>
<td>bool : 불리언</td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
</tbody></table>
<h2 id="컬렉션">컬렉션</h2>
<ul>
<li>여러 값을 하나의 변수에 저장할 수 있는 타입</li>
</ul>
<h2 id="list타입">List타입</h2>
<ul>
<li>리스트 타입은 여러값을 순서대로 한 변수에 저장할 때 사용된다.
리스트명[인덱스] 형식으로 특정원소에 접근이 가능하다.</li>
</ul>
<p><strong>add( )</strong></p>
<pre><code class="language-dart">void main() {
List&lt;String&gt; flutterStudy = [&#39;베짱이&#39;,&#39;Hun&#39;,&#39;Jake&#39;,&#39;이지훈&#39;];
print(flutterStudy);
flutterStudy.add(&#39;헤이즐&#39;);
print(flutterStudy);
}</code></pre>
<ul>
<li><strong>When?</strong>  List에 값을 추가할 때 사용</li>
<li><strong>How?</strong>  추가하고 싶은 값을 매개변수에 입력한다.</li>
</ul>
<p><strong>Where( )</strong></p>
<pre><code class="language-dart">void main() {
final HunflutterStudy = flutterStudy.where(
(name) =&gt; name == &#39;Hun&#39; || name == &#39;이지훈&#39;,
);


print(HunflutterStudy);
print(HunflutterStudy.toList());
}</code></pre>
<ul>
<li><strong>When?</strong> List에 있는 값을 순회하며 조건을 충족시키는 값만 필터링하는데 사용</li>
<li><strong>How?</strong> 매개변수에 함수를 입력하면, 입력된 함수는 기존값을 하나씩 매개변수로 받음. True일시에는 값을 유지하고 false일시 값을 버린다.</li>
</ul>
<p><strong>map()</strong></p>
<pre><code class="language-dart">void main() {
List&lt;String&gt; flutterStudy = [&#39;베짱이&#39;,&#39;Hun&#39;,&#39;Jake&#39;,&#39;이지훈&#39;, &#39;헤이즐&#39;];
final allMembers = flutterStudy.map(
(name) =&gt; &#39;스터디멤버 $name&#39;,
);
print(allMembers);
}</code></pre>
<ul>
<li><p><strong>When?</strong> List에 있는 값을 순서대로 순회하며 값을 변경</p>
</li>
<li><p><strong>How?</strong> 매개변수에 함수를 입력하면, 입력된 함수는 기존값을 하나씩 매개변수로 받음. 반환값이 현재값을 대체.</p>
</li>
</ul>
<p><strong>reduce()</strong></p>
<pre><code class="language-dart">void main() {
List&lt;String&gt; flutterStudy = [&#39;베짱이&#39;,&#39;Hun&#39;,&#39;Jake&#39;,&#39;이지훈&#39;, &#39;헤이즐&#39;];
final allMembers = flutterStudy.reduce((value, element) =&gt; value + &#39;,&#39; + element);
print(allMembers);
}</code></pre>
<ul>
<li><p><strong>When?</strong> List에 있는 값을 순서대로 순회하며 <strong>값을 쌓아가며</strong> 입력된 함수를 실행 (나머지 함수는 Iterable을 반환했으나 <code>reduce</code> 함수는 List 멤버와 같은 타입을 반환</p>
</li>
<li><p><strong>How?</strong> 매개변수에 함수를 <strong>두개</strong>를 입력하면, 순회를 시작할 때 첫번째 매개변수(<code>value</code>)와 두번째 매개변수(<code>element</code>)를 각각 받는다. 첫 순회 이후 반환된 기존 값을 첫번째 매개변수에, 그 다음값을 두번째 매개변수로 입력 받으며 리스트 내부의 값들을 점차 더해가는 기능으로 사용한다.</p>
</li>
</ul>
<p><strong>fold( )</strong></p>
<pre><code class="language-dart">void main() {
List&lt;String&gt; flutterStudy = [&#39;베짱이&#39;,&#39;Hun&#39;,&#39;Jake&#39;,&#39;이지훈&#39;, &#39;헤이즐&#39;];
final allMembers = flutterStudy.fold&lt;int&gt;(
0, (value, element) =&gt; value + element.length);
print(allMembers);
}</code></pre>
<ul>
<li><p><strong>When?</strong> reduce() 함수와 실행논리는 똑같으나 reduce() 함수는 실행되는 요소의 타입이 일치해야한다면 fold() 함수는 어떤 타입이든 반환 가능.</p>
</li>
<li><p><strong>How?</strong> 첫번째 매개변수에 시작할 값 지정, 두번째 매개변수에 작동하는 함수를 입력한다. 예시를 보면 value는 최초 순회때의 첫번재 값을 사용하고 element는 람다식으로 최초순회때 초기값인 0이 입력되고 이후에는 기존 순회 반환값을 받아온다. </p>
</li>
</ul>
<h2 id="map">Map</h2>
<pre><code class="language-dart">void main() {
Map&lt;String, String&gt; animals = {
&#39;dog&#39; : &#39;강아지&#39;,
&#39;cat&#39; : &#39;고양이&#39;,
&#39;horse&#39; : &#39;말&#39;,
&#39;rat&#39; : &#39;쥐&#39;,
 };

print(animals[&#39;dog&#39;]);
print(animals[&#39;cat&#39;]);
}</code></pre>
<ul>
<li><p><strong>When?</strong> List가 순회와 순차의 방식으로 값을 받아온다면 Map은 지도에서 도착지와 출발지로 경로를 찾듯 키와 값의 짝을 저장해 원하는 값을 빠르게 찾는데 중점을 둔다.</p>
</li>
<li><p><strong>How?</strong> Map&lt;키의 타입, 값의 타입&gt; 맵이름 형식으로 생성</p>
</li>
</ul>
<h3 id="map-키-값-반환">Map 키, 값 반환</h3>
<pre><code class="language-dart">void main() {
Map&lt;String, String&gt; animals = {
&#39;dog&#39; : &#39;강아지&#39;,
&#39;cat&#39; : &#39;고양이&#39;,
&#39;horse&#39; : &#39;말&#39;,
&#39;rat&#39; : &#39;쥐&#39;,
 };

print(animals.keys);
print(animals.values);
}</code></pre>
<ul>
<li><code>Map</code> 타입은 키와 값을 모두 반환받을 수 있다. 변수에 key와 value 게터를 실행하면 된다.</li>
</ul>
<p><strong>Set타입</strong></p>
<pre><code class="language-dart">void main() {
Set&lt;String&gt; flutterStudy = {&#39;베짱이&#39;,&#39;Hun&#39;,&#39;Jake&#39;,&#39;이지훈&#39;, &#39;헤이즐&#39;};
print(flutterStudy);
print(flutterStudy.contains(&#39;헤이즐&#39;));
print(flutterStudy.toList());

List&lt;String&gt; flutterMems = [&#39;베짱이&#39;, &#39;Hun&#39;, &#39;Jake&#39;];
print(Set.from(flutterMems));
}</code></pre>
<ul>
<li><p><strong>When?</strong> 중복을 방지해 유일한 값들만 존재하는 것을 보장하기에 각 값의 유일함을 보장받고 싶을 때 사용</p>
</li>
<li><p><strong>How?</strong> Set.toList를 사용하면 기존 데이터를 유지한채 Set을 List 타입으로 변환 가능하며, 키와 값을 따로 리스트로 받고 싶을 때는 Keys.toList(), values.toList()를 사용하면 된다. </p>
</li>
</ul>
<p><strong>Enum</strong></p>
<pre><code class="language-dart">enum Status{
approved,
pending,
rejected,
}
void main(){
Status status = Status.approved;
print(status);
}</code></pre>
<ul>
<li><strong>When?</strong> 한변수의 값을 몇가지 옵션으로 제한하며, 선책지가 제한적일 때 사용된다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[인터넷에서 정보는 어떤 모습일까?]]></title>
            <link>https://velog.io/@hazel_1130/%EC%9D%B8%ED%84%B0%EB%84%B7%EC%97%90%EC%84%9C-%EC%A0%95%EB%B3%B4%EB%8A%94-%EC%96%B4%EB%96%A4-%EB%AA%A8%EC%8A%B5%EC%9D%BC%EA%B9%8C-griz9e6f</link>
            <guid>https://velog.io/@hazel_1130/%EC%9D%B8%ED%84%B0%EB%84%B7%EC%97%90%EC%84%9C-%EC%A0%95%EB%B3%B4%EB%8A%94-%EC%96%B4%EB%96%A4-%EB%AA%A8%EC%8A%B5%EC%9D%BC%EA%B9%8C-griz9e6f</guid>
            <pubDate>Thu, 07 Sep 2023 08:44:37 GMT</pubDate>
            <description><![CDATA[<ul>
<li>부족한 cs 지식 채우기 (2)</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hazel_1130/post/edf8c412-e2dd-4667-8215-3b40c93c1d4e/image.png" alt=""></p>
<h2 id="패킷이란">패킷이란?</h2>
<ul>
<li><p>패킷</p>
<ul>
<li>발송정보와 콘텐츠의 두체계로 이루어진 데이터 단위</li>
<li>인터넷 프로토콜은 모든 데이터의 구조를 해체 후, 여러개의 패킷으로 쪼개 전송한다<ul>
<li>주소 태그</li>
<li>각 패킷에는 발신 및 수신 주소가 포함된 주소 태그가 지정</li>
<li>패킷 헤더</li>
</ul>
</li>
<li>패킷의 크기와 유형을 설명하는 역할</li>
</ul>
<h2 id="패킷은-무엇으로-구성될까">패킷은 무엇으로 구성될까?</h2>
<ul>
<li>패킷은 컴퓨터 언어 -&gt; 이진수 데이터로 구성</li>
<li>네트워크 디바이스는 이진수로만 된 정보를 전송, 매체에 따라 다양하게 인코딩함<h2 id="패킷-전송">패킷 전송</h2>
</li>
<li>이진수 신호는 주파수 변조라는 과정을 걸쳐 전송</li>
</ul>
<p><img src="https://velog.velcdn.com/images/hazel_1130/post/88a58a7f-3b3a-4c98-ba49-90c28bcf07e9/image.png" alt=""></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[불필요한 리소스 제거를 통한 성능 최적화]]></title>
            <link>https://velog.io/@hazel_1130/%EB%B6%88%ED%95%84%EC%9A%94%ED%95%9C-%EB%A6%AC%EC%86%8C%EC%8A%A4-%EC%A0%9C%EA%B1%B0%EB%A5%BC-%ED%86%B5%ED%95%9C-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94</link>
            <guid>https://velog.io/@hazel_1130/%EB%B6%88%ED%95%84%EC%9A%94%ED%95%9C-%EB%A6%AC%EC%86%8C%EC%8A%A4-%EC%A0%9C%EA%B1%B0%EB%A5%BC-%ED%86%B5%ED%95%9C-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94</guid>
            <pubDate>Thu, 11 May 2023 15:14:28 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/hazel_1130/post/beb3f1db-1f80-45cb-a041-cbafa90fb532/image.png" alt="">
웹 애플리케이션에서는 성능 최적화가 중요한 이슈 중 하나입니다. 성능이 좋지 않은 웹 애플리케이션은 사용자 경험을 저하시키고, 더 나아가 서비스 이용에 대한 만족도를 떨어뜨릴 수 있습니다. 웹 프론트앤드 환경에서는 각종 데이터들을 주고 받으며 화면을 띄우고 화면을 업데이트하는 과정 속에서 비용을 소모하며, 웹 프론트앤드 성능 개선을 위해서 웹 개발자는 최소한의 비용으로 가장 빠른 시간에에 사용자가 불편함을 느끼지 않는 최적의 화면을 띄워야만 합니다.</p>
<p>이를 위해 모행 대다수의 로직은 비동기로 작성되었으나 어느 정도 개발이 완성된 후 우리는 더 나은 성능을 위해 불필요한 리소스를 제거하는 방안을 모색했습니다. </p>
<p>아래는 그 중 하나였던 북마크 추가에 대한 코드의 일부입니다.</p>
<pre><code class="language-tsx">const addBookmark = () =&gt; {
    {...}
    const response = async () =&gt; {
      await axios.post(
        `/api/place/bookmark/${placeId}`,
        {...}
        ;
    };
    response()
{...}
        .then(() =&gt; {
          **appDispatch(getPlaceBookmark(accessToken));**
        });
    });
  };</code></pre>
<pre><code class="language-tsx">export const getPlaceBookmark = createAsyncThunk(
  &quot;mypage/placebookmark&quot;,
  async (token: string) =&gt; {
    const response = await axios.get(`/api/myPage/place/bookMark`, {
      {...}
    return response.data.data;
  }
);</code></pre>
<p>위 코드는 여행지 페이지에서 북마크 추가 요청을 보내고, 요청에 대한 서버의 응답이 성공적으로 왔을 때, 마이페이지에서 사용하는 북마크 상태를 API 호출을 통해 업데이트하는 코드입니다. 그러나 저는 이 방식이 리소스적으로 비효율적이라고 생각했습니다.</p>
<p>예를 들어, 사용자가 북마크 On/Off를 연속으로 시도한 경우 매 시도마다 현재 사용자가 보고 있는 여행지 페이지에서 북마크 상태를 변경하는 것은 필연적입니다. 그러나 현재 렌더링이 되어있지 않은 마이페이지의 북마크 상태를 업데이트하는것은 불필요하게 리소스를 낭비한다고 생각했습니다.</p>
<pre><code class="language-tsx">useEffect(() =&gt; {
appDispatch(getCourseBookmark(accessToken));
appDispatch(getPlaceBookmark(accessToken));
}, []);</code></pre>
<p>따라서, 저는 마이페이지의 북마크 컴포넌트 가 렌더링될 때만 북마크 상태를 업데이트하도록 결정했습니다.  해당 코드가 추후 수정된 코드입니다. 이 로직은  <strong><code>useEffect</code></strong> 훅을 통해  마이페이지 컴포넌트가 렌더링될 때 동작하며,  <strong><code>getCourseBookmark</code></strong>와 <strong><code>getPlaceBookmark</code></strong> 로 북마크 상태를 업데이트합니다.  </p>
<p>마이페이지가 아닌 이외의 페이지에서  북마크 상태를 변경할 때마다 API를 호출하는 것이 아니라, 마이페이지가 렌더링되어 해당 상태가 필요할 때에만 API 호출과 상태 업데이트가 되도록 구현하는 방식이 리소스를 더욱 효율적으로 사용할 수 있다고 느꼈기 때문에 해당 방식으로 코드 로직을 구현하게 됐습니다.</p>
<p> 리소스와 관련한 논의중에 모행은 방대한 데이터를 담고 있는 정보성 웹 사이트기에 다량의 데이터를 DB에서 불러올 때 많은 시간이 소요됩니다. 따라서 현재 렌더링 되어있지 않아 당장에 불필요한 상태(state) 일지라도 서버의 데이터가 변경될 때 마다 상태 업데이트를 진행하는 것이 낫지 않겠냐는 의견도 있었습니다. 하지만 해당 경우에는 불필요한 API 호출과 상태 업데이트가 빈번히 일어날 수 있다고 판단해 현재의 방식을 차용해 성능을 최적화시키는 방안으로 코드를 작성하였습니다.</p>
<p> 현재도 모행은 계속 리팩토링 중이며 성능 최적화와 반응형 CSS를 적용해나가고 있습니다. 개발자로서  한 프로젝트를 빠르게 완료하는 것도 중요하지만 끊임없이 더 나은 방향을 향해 고민하고 적용하여 완벽한 프로젝트를 완성하는 것이 중요하다는 것을 느꼈습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Pagination Custom Hook 구현]]></title>
            <link>https://velog.io/@hazel_1130/Pagination-Custom-Hook-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@hazel_1130/Pagination-Custom-Hook-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Wed, 03 May 2023 17:25:40 GMT</pubDate>
            <description><![CDATA[<p>모행은 여행지 정보와 코스에 대한 정보를 주는 정보성 짙은 여행 플랫폼이었기에 페이지네이션이 사용되는 부분이 많았습니다. 해당 페이지네이션을 커스텀 훅으로 구현해 쓸 수 있다면 편리할 것 같아 직접 커스텀 훅으로 구현해보고자 했습니다.</p>
<h3 id="사용하는-곳ui"><strong>사용하는 곳(UI)</strong></h3>
<pre><code class="language-jsx">//totalPage = {총 페이지 수} Props로 전달
&lt;Pagebar totalPage={totalPages}/&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/hazel_1130/post/14d5afc3-a435-4336-82c7-7da2f867d727/image.png" alt=""></p>
<h3 id="페이지-변경-방법">페이지 변경 방법</h3>
<pre><code class="language-tsx">//store 에 page 로 전역 state로 관리중이며 해당 page 변경 시 현재 페이지 변경 가능

//PlaceList.tsx

dispatch(setPage(2)); // 현재 선택한 페이지 2페이지로 변경

// ex) PlaceList.tsx 에서의 사용 방식

useEffect(() =&gt; {
    const response = async () =&gt; {
      const placeResponse = await axios
        .get(`/places`, {
          params: {
            areaCode: areacode,
            page: page,
          },
          withCredentials: true,
        })
        .then((res) =&gt; dispatch(setPlace(res.data.data)));
    };
    response();
  }, [page]);</code></pre>
<p>이 코드는 전역 상태 관리 라이브러리인 Redux를 사용하여 <strong><code>page</code></strong> 를 전역 상태로 관리합니다.  <strong><code>useEffect</code></strong> 훅을 사용하여 <strong><code>page</code></strong> 가 변경될 때마다 해당 페이지의 데이터를 가져오는 API 요청을 보내고, 해당 데이터들로 상태를 업데이트합니다.</p>
<pre><code class="language-tsx">const page = useSelector((state: RootState) =&gt; state.page.page);
  const maxPage = Math.ceil(totalPage?.totalPage / 10);

  const [pageNumbers, setPageNumber] = useState&lt;number[]&gt;([]);

  const [pagePer, setPagePer] = useState(0);

  useEffect(() =&gt; {
    const array = [];
    for (let i = 1; i &lt;= 10; i++) {
      array.push(pagePer * 10 + i);
    }
    setPageNumber(array);
    dispatch(setPage(pagePer * 10 + 1));
  }, [pagePer]);</code></pre>
<p>먼저, 사용자에게 <strong><code>totalPage</code></strong>를 prop으로 전달받습니다. 해당 prop을 통해 <strong><code>maxPage</code></strong> 를 계산하여 전체 페이지를 10개씩 분리해줍니다. </p>
<p>그리고 <strong><code>useState</code></strong> 훅을 사용해서 <strong><code>pagePer</code></strong>와 <strong><code>pageNumbers</code></strong>를 상태로 관리합니다. <strong><code>pagePer</code></strong>는 현재 보여지고 있는 페이지 그룹이 몇 번째 그룹 인지를 의미합니다. <strong><code>pageNumbers</code></strong>는 현재 <strong><code>pagePer</code></strong> 기준으로 10개의 페이지 번호를 담은 배열입니다. 예를 들어 <strong><code>pagePer</code></strong> 가 5라면 <strong><code>pageNumbers</code></strong> 는 51~60 까지의 배열을 상태로 가집니다. </p>
<p><strong><code>prev</code></strong> , <strong><code>next</code></strong> 를 통해 <strong><code>pagePer</code></strong> 의 상태를 업데이트 하고 <strong><code>useEffect</code></strong> 훅을 사용하여 <strong><code>pagePer</code></strong>의 값이 변경될 때마다 <strong><code>pageNumbers</code></strong> 배열을 업데이트하고, Redux store의 현재 페이지를 해당 <strong><code>pageNumbers</code></strong> 첫 페이지로 변경합니다.</p>
<p><strong><code>pageNumbers</code></strong> 배열에는 <strong><code>totalPage</code></strong>보다 큰 페이지 번호가 들어갈 수 없도록 조건을 걸어 렌더링합니다.  </p>
<h3 id="배운-점">배운 점</h3>
<p>페이지네이션 커스텀 훅은 중복 로직 코드를 피해 팀원 분들과 효율적인 프로젝트를 진행하고 싶었기에, 그리고 커스텀 훅을 코드로 구현해보고 싶다는 욕심 때문에 시작하게 됐습니다.  커스텀 훅의 장점은 컴포넌트 로직을 재사용 가능하게 만듬으로써 코드의 중복을 제거하고, 필요한 곳에서 재사용할 수 있으며, 관심사를 분리하여 관리가 용이해진다는 점에 있습니다.  </p>
<p>타 프론트엔드분의 코드에서  제 커스텀 훅을 마주칠 때엔 왠지 모를 뿌듯함이 느껴졌습니다. 커스텀 훅은 어떤 로직을 수행하는 코드인지 쉽게 알아차릴 수 있기 때문에 다른 개발자들과 협업에도 이점을 가져다준다는 것을 배울 수 있었던 좋은 시간이었습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Redux, Redux-Toolkit이 필요한 이유와 실제 구현 방법]]></title>
            <link>https://velog.io/@hazel_1130/Redux-Redux-Toolkit%EC%9D%B4-%ED%95%84%EC%9A%94%ED%95%9C-%EC%9D%B4%EC%9C%A0%EC%99%80-%EC%8B%A4%EC%A0%9C-%EA%B5%AC%ED%98%84-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@hazel_1130/Redux-Redux-Toolkit%EC%9D%B4-%ED%95%84%EC%9A%94%ED%95%9C-%EC%9D%B4%EC%9C%A0%EC%99%80-%EC%8B%A4%EC%A0%9C-%EA%B5%AC%ED%98%84-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Mon, 01 May 2023 16:19:29 GMT</pubDate>
            <description><![CDATA[<h2 id="📖리덕스란">📖리덕스란?</h2>
<p><img src="https://velog.velcdn.com/images/hazel_1130/post/aa1b8ca3-ee38-4bbc-a983-7356cc91481a/image.png" alt="https://velog.velcdn.com/images/hazel_1130/post/aa1b8ca3-ee38-4bbc-a983-7356cc91481a/image.png"></p>
<p>리덕스의 핵심은 중앙 저장소인 store에서 상태를 효율적으로 관리하여 각 컴포넌트로 바로 전달하는 것입니다. 이를 통해 개발자들은 더 적은 코드를 작성하고, 상태 관리에 더 집중할 수 있습니다.</p>
<p>우리가 만드는 컴포넌트에는 공통적으로 관리되는 데이터가 존재합니다. 이러한 공통데이터를 한곳에서 관리하지 않고 필요한 컴포넌트마다 새로 코드를 작성하게 된다면 코드의 가독성과 유지보수성이 떨어지고 복잡성이 커질 것입니다. 때문에 서비스 규모가 크고 공통적으로 관리하는 데이터가 많을수록 적합한 상태 관리 라이브러리를 이용해 코드를 작성하는 것이 효율적입니다.</p>
<p> 리덕스 외에도 다른 상태관리 라이브러리(Context API, mobx 등)이 있지만 결과적으로 리덕스를 사용하기로 한 것은 성능 최적화나 비동기 지원 난이도의 측면에서 리덕스가 우월하다고 생각했고 리덕스만의 디버깅 툴로 인한 효과적인 에러를 잡을 수 있다는 장점 때문이었습니다. 또한 모행 프로젝트에서 TypeScript를 도입하고 있기 때문에 컴포넌트에서 상태를 조회할 때나 액션생성 함수를 사용 할 때  일일이 찾아보지 않고 자동완성으로 찾을 수 있다는 점도 매력적으로 다가왔습니다. </p>
<p> 물론 장점만이 있는 것은 아닙니다. 리덕스의 가장 큰 단점은 보일러 플레이트를 많이 쓰게 된다는 것입니다. 때문에 상대적으로 코드량이 간소한 mobx의 사용도 고민했지만, 리덕스 개발팀에서 릴리즈한 Redux Toolkit이라는 라이브러리가 이같은 문제를 포함한 기존 리덕스가 가진 여러 문제를 해결하고 있다는 것을 알게 되었습니다. </p>
<h2 id="✨왜-리덕스-툴킷인가">✨왜 리덕스 툴킷인가?</h2>
<p><img src="https://velog.velcdn.com/images/hazel_1130/post/cbd42d83-04b2-4f23-b26a-4954633a3b5b/image.png" alt=""></p>
<p>리덕스는 아무리 작은 기능이라도 액션 타입, 액션 생성함수, 리듀서를 기술해야 합니다. 또한 코드 단순화를 위해 redux-actions을, typescript 지원을 위해 typesafe-actions, 불변성 간소화 코드 작성을 위해 immer 등등의 라이브러리를 별도로 도입해야 해야 하기에 팀프로젝트의 규모에서 리덕스를  쓰다 보면 굳이 이렇게까지 해야 하나 하는 생각이 듭니다. 리덕스 툴킷은 이런 문제를 해결하기 위해 리덕스 개발팀에서 공식적으로 제공하는 라이브러리입니다. </p>
<p>공식문서에서 말하는 <strong>리덕스 툴킷의 특징</strong>은 다음과 같습니다.</p>
<ol>
<li><p><strong>Simple</strong> : 스토어 설정, 리듀서 생성, 변경 불가능한 업데이트 로직 등과 같은 일반적인 사용 사례를 단순화하는 유틸리티 포함</p>
</li>
<li><p><strong>Opinionated</strong> : 스토어 설정을 위한 좋은 기본값 제공, 가장 일반적으로 사용되는 Redux addons 내장</p>
</li>
<li><p><strong>Powerful</strong> : Immer 및 Autodux와 같은 라이브러리에서 영감을 얻어 mutative하게 작성해도 불변성 로직으로 작성 가능, 전체 상태 슬라이스를 자동으로 생성 가능</p>
</li>
<li><p><strong>Effective</strong> : 적은 코드로 많은 작업 수행 가능</p>
<p>아래는 실제 프로젝트에서 리덕스와 리덕스 툴킷을 사용해 구현한 코드입니다.</p>
</li>
</ol>
<h2 id="🔎실제-프로젝트에서-구현한-코드">🔎실제 프로젝트에서 구현한 코드</h2>
<ul>
<li><p>구현 코드 (PlaceBookmarkSlice )</p>
<pre><code class="language-tsx">  export const PlaceBookmarkSlice = createSlice({
    name: &quot;placeBookmark&quot;,
    initialState,
    reducers: {},
    extraReducers: (builder) =&gt; {
      builder.addCase(getPlaceBookmark.pending, (state) =&gt; {
      });
      builder.addCase(getPlaceBookmark.fulfilled, (state, action) =&gt; {
        state.data = action.payload;
      });
      builder.addCase(getPlaceBookmark.rejected, (state) =&gt; {
      });
    },
  });</code></pre>
</li>
</ul>
<p>해당 코드는 Redux Toolkit을 사용하여 생성한 PlaceBookmarkSlice 리듀서 코드입니다. 여행지 북마크 api에 가져온 정보들을 저장하거나 관리하기 위해서 만든 공간이며, 이 리듀서는 Redux store에서 placeBookmark라는 이름으로 사용됩니다. createAsyncThunk() 함수를 사용하여 비동기 작업 처리를 했기 때문에 자동으로 pending, fulfilled, rejected 액션을 생성하여 비동기 작업의 성공 여부를 처리할 수 있습니다.</p>
<ul>
<li><p>구현 코드  (getPlaceBookmark)</p>
<pre><code class="language-tsx">  export const getPlaceBookmark = createAsyncThunk(
    &quot;mypage/placebookmark&quot;,
    async (token: string) =&gt; {
      const response = await axios.get(`/api/myPage/place/bookMark`, {
        headers: {
          &quot;Access-Token&quot;: token,
        },
        withCredentials: true,
      });
      return response.data.data;
    }</code></pre>
</li>
</ul>
<p> 백엔드에서 IsBookmarked의 여부를 가져오는 코드입니다.  </p>
<p>해당 코드는 Redux Toolkit의 createAsyncThunk을 이용하여 비동기 액션을 생성하는 예시입니다. 앞서 말했듯이 createAsyncThunk은 Redux Toolkit에서 제공하는 간편한 방법으로 비동기 액션을 생성할 수 있게 해주는 함수입니다. 이 함수를 이용하면 해당 액션에 대한 세 가지 상태pending, fulfilled, rejected를 자동으로 관리해주며, 비동기 처리 중에 발생할 수 있는 예외 상황도 처리할 수 있습니다.</p>
<h2 id="👍-결론">👍 결론</h2>
<p>위와 같이  리덕스 툴킷을 적용했을 때 실제로 느낀 장점은 이렇습니다.</p>
<p>우선, 기존 리덕스를 쓰면서 불필요하다고 느꼈던 코드량이 대폭 줄어들었습니다. createSlice를 만들어두면 선언된 슬라이스 이름을 따라서 리듀서와 그리고 그것에 상응하는 액션 생성자와 액션 타입을 자동으로 생성해주기 때문입니다. 또한 createAsyncThunk 함수를 사용하여 비동기 작업을 보다 쉽게 관리할 수 있었습니다.  전체적으로 리덕스의 단점을 잘 보완한 라이브러리 툴이라는 생각이 들어 기쁜 마음으로 함께 할 수 있었습니다!</p>
<ul>
<li><p>참고자료</p>
<p>  <a href="https://redux-toolkit.js.org/">Redux-Toolkit 공식문서</a></p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Coddit] useEffect와 Custom Hook]]></title>
            <link>https://velog.io/@hazel_1130/useEffect%EC%99%80-Custom-Hook</link>
            <guid>https://velog.io/@hazel_1130/useEffect%EC%99%80-Custom-Hook</guid>
            <pubDate>Sat, 18 Mar 2023 10:01:28 GMT</pubDate>
            <description><![CDATA[<h3 id="💥에러-발생">💥에러 발생</h3>
<p><img src="https://velog.velcdn.com/images/hazel_1130/post/ae5f1c85-245e-43e3-ad3c-e6fc6539f83a/image.png" alt=""></p>
<p>Coddit 프로젝트에서는 포스트를 클릭하면 해당 포스트의 내용과 댓글 등을 modal로 띄우는 방식을 사용하고 있다. 이를 위해서는 클릭한 포스트의 내용과 댓글들을 state로 관리해야 했다.
 사용자가 투표나 댓글 작성, 댓글 투표 등의 기능을 사용할 때 해당 state들이 변경되면 함수를 호출해 페이지가 자연스럽게 변경되게 기능을 구현했고, 이 때, useEffect를 사용하여 해당 state들이 변경될 때마다 호출하는 방식을 사용했다.</p>
<hr>
<h3 id="💦에러-원인">💦에러 원인</h3>
<p>[에러 발생 초기 코드]</p>
<pre><code class="language-js">
    useEffect(() =&gt; {
     modalContext.setState(currentPost[0]);
    }, [currentPost]);
  };
</code></pre>
<p> currentPost가 변경될 때마다, useEffect의 첫 번째 인수로 정의된 함수가 실행되며, 해당 함수는 modalContext에서 setState 함수를 호출하여 선택된 게시물의 첫 번째 요소를 모달 창에 표시하는 코드다. 하지만 첫 렌더링 시 오류가 발생해 렌더링이 되지 않는 문제가 발생했다. 구글링을 해보니 useEffect가 첫 렌더링 때도 실행된다는 원인을 찾을 수 있었다. </p>
<hr>
<h3 id="💫해결-방안">💫해결 방안</h3>
<p>이를 해결하기 위해 Custom Hook을 사용하기로 했다. useEffect를 대신하여 사용할 함수를 만들어야 했는데, Custom Hook을 사용해 useEffect와 유사하게 작동하지만 최초 렌더링 시에는 실행되지 않도록 설정할 수 있었다. </p>
<p>최종적으로 변경된 코드는 다음과 같다.</p>
<pre><code class="language-js">const useDidMountEffect = (func, deps) =&gt; {
  const didMount = useRef(false);

  useEffect(() =&gt; {
    if (didMount.current) func();
    else didMount.current = true;
  }, deps);
};

useDidMountEffect(() =&gt; {
  modalContext.setState(currentPost[0]);
}, [currentPost]);</code></pre>
<p>useRef Hook을 사용하여 최초 렌더링 시에는 false를 반환, 최초 렌더링 이후에는 true를 반환하도록 설정해 이후부터는 useEffect와 동일하게 작동하게 만들었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next.js! 왜 쓰는 걸까? (CSR vs SSR)]]></title>
            <link>https://velog.io/@hazel_1130/Next.js-%EC%99%9C-%EC%93%B0%EB%8A%94-%EA%B1%B8%EA%B9%8C-CSR-vs-SSR</link>
            <guid>https://velog.io/@hazel_1130/Next.js-%EC%99%9C-%EC%93%B0%EB%8A%94-%EA%B1%B8%EA%B9%8C-CSR-vs-SSR</guid>
            <pubDate>Fri, 10 Mar 2023 17:19:20 GMT</pubDate>
            <description><![CDATA[<h2 id="📖-intro">📖 Intro</h2>
<hr>
<p>  프로젝트가 본격적으로 진행되기 전에 우리는 많은 것을 결정해야 한다. 커뮤니티의 목적은 무엇인지, 어떻게 설계해야하는지, 회의는 어떤 방식으로 진행할지. 많은 항목들이 있지만 오늘 우리가 얘기하고자 하는 것은 Next.js 에 대한 이야기다. </p>
<p>  저번 팀 프로젝트에서는 프론트엔드에서 많이 쓰이는 기술이 React이기 때문에 해당 라이브러리를 이용한 프로젝트를 하고 싶었고, 해당 기술의 숙련도를 익히고 심화적으로 증진시키기 위함이 주 목적이었다. 그러나 이번에는 다인원 프로젝트였고, 클론 코딩이 아닌 명확한 목적이 있는 여행 웹 커뮤니티였기에 렌더링 방식과 기술 사용에 대해 고민이 필요하다는 생각이 들었다.  </p>
<p> 본 글에서는 Next.js를 설명할때 빠질 수 없는 클라이언트 측 렌더링(CSR)과 서버 측 렌더링(SSR)에 대해 간단히 살펴보고, 왜 모행 프로젝트의 프레임워크로 Next.js가 적합한지 살펴볼 것이다. </p>
<h2 id="📕-csr">📕 CSR</h2>
<hr>
<p><img src="https://velog.velcdn.com/images/hazel_1130/post/4de7025a-80ae-403d-be5c-de0aa6396b5c/image.png" alt=""></p>
<p>클라이언트 사이드 렌더링(Client Side Rendering, CSR)은 브라우저가 페이지의 HTML을 요청한 다음 자바스크립트를 로드하여 클라이언트 사이드에서 콘텐츠를 렌더링하는 렌더링 방법이다.
더 빠른 후속 페이지 로드, 더 나은 상호 작용 및 더 동적인 사용자 경험이 CSR의 장점이라고 할 수 있다. 그러나 초기 페이지 로드 시간이 길어지고 접근성에 문제가 발생할 수 있다는 점 때문에 검색 엔진 최적화(SEO)에는 부적합할 수도 있다.</p>
<p>때문에 CSR은 소셜 미디어 플랫폼이나 메시징 앱과 같이 많은 상호작용과 실시간 데이터 업데이트가 필요한 웹 애플리케이션에 보다 더 적합하다.</p>
<h2 id="📘-ssr">📘 SSR</h2>
<hr>
<p><img src="https://velog.velcdn.com/images/hazel_1130/post/fb012ce1-df61-4b47-bff1-ef78ae2477cf/image.png" alt=""></p>
<p>SSR(Server-Side Rendering)은 서버가 완전히 렌더링된 HTML 페이지를 브라우저로 보내는 렌더링 방법이다. 서버는 초기 페이지의 렌더링을 처리하여 첫 번째 바이트까지의 시간을 줄이고 검색 엔진에 내용을 볼 수 있으므로 검색 엔진 최적화에 적합한 렌더링 방식이다.</p>
<p>첫 번째 바이트까지의 시간이 짧으며, 액세스 용이성이 향상되는 등 확실한 장점이 있자만 후속 페이지 로드 속도가 느리고, 상호 작용성이 저하되는 등의 단점도 있다.</p>
<p>때문에 SSR은 전자 상거래 웹 사이트 또는 뉴스 웹 사이트와 같이 SEO의 우선 순위를 지정하거나 정적 콘텐츠가 많거나 첫 번째 바이트까지의 시간이 빠른 웹 사이트에 적합하다.</p>
<h2 id="📚-nextjs">📚 Next.js</h2>
<hr>
<p>Next.js는 최근 가장 인기 있는 React 기반 프레임워크라고 봐도 무방하다. 실제로 많은 공고문에 해당 기술을 우대사항으로 집어넣는 기업들을 많이 봤을 것이다. 나또한 다른 사람들의 깃 프로젝트를 보며 Next.js를 처음 알게 됐고, Next.js가 왜 그렇게 화제일까? 하는 궁금증이 들었다. </p>
<p>우선 Next.js 작동 방식은 다음과 같다. </p>
<blockquote>
<ol>
<li>사용자가 초기에 Server에 페이지 접속을 요청한 경우 SSR방식으로 렌더링 될 HTML을 보낸다.</li>
<li>브라우저에서 JS를 다운로드 받고 React를 실행한다.</li>
<li>사용자가 페이지와 상호작용을 하며 다른 페이지로 이동할 경우 CSR방식으로 Server가 아닌 브라우저에서 처리한다.</li>
</ol>
</blockquote>
<p> 작동 방식을 보면 알 수 있듯이 Next.js 가장 큰 장점은 앞서 언급한 CSR과 SSR의 이점을 모두 결합했다는 것이다. 기본적으로 서버 측 렌더링을 사용할 수 있지만 필요한 경우 클라이언트 측 렌더링도 허용한다. 한마디로 말해서 SSR에 최적화된 프레임워크라고 볼 수 있겠다. </p>
<p>또한 Next.js는 서버측 렌더링에 대한 내장 지원을 하기 때문에 검색 엔진 최적화 성능을 높이고 노출 순위를 쉽게 개선할 수 있으며, 이미지, 코드 분할 및 느린 로딩을 자동으로 최적화하여 빠르고 응답성이 높은 웹 페이지를 구축할 수 있게 해준다.  </p>
<p>때문에 여행지 정보와 코스 정보 같은 정적 컨텐츠가 비중을 많이 차지하고, 사용자들이 많이 유입될수록 (실제로 상업적인 배포를 한다면) 검색 엔진에 자주 노출될수록 더 활성화될 수 있는 여행 커뮤니티 웹사이트 모행에는 Next.js가 적합하다는 결론이 나왔다. </p>
<p>물론 사람들이 많이 이용하기 때문에, 혹은 취업하고 싶은 기업이 해당 경험을 우대해주기 때문에 같은 이유로 기술이나 툴을 선택하는 것도 합리적인 결정이라고 생각한다. 하지만 두번째 프로젝트이기도 하고, 제대로 해보고 싶은 마음이 컸기에 더 유의미한 고민과 결정을 하고 싶었다. 최근 Next.js가 혁신적인 개편(ㅠㅠ)을 하면서 살짝 멘붕이 왔지만 해당 부분에 대한 것은 후에 다루기로 하겠다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[원티드 3월 프리온보딩 챌린지(2)]]></title>
            <link>https://velog.io/@hazel_1130/%EC%9B%90%ED%8B%B0%EB%93%9C-3%EC%9B%94-%ED%94%84%EB%A6%AC%EC%98%A8%EB%B3%B4%EB%94%A9-%EC%B1%8C%EB%A6%B0%EC%A7%802</link>
            <guid>https://velog.io/@hazel_1130/%EC%9B%90%ED%8B%B0%EB%93%9C-3%EC%9B%94-%ED%94%84%EB%A6%AC%EC%98%A8%EB%B3%B4%EB%94%A9-%EC%B1%8C%EB%A6%B0%EC%A7%802</guid>
            <pubDate>Tue, 28 Feb 2023 05:17:21 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>사전 과제 제출 결과물</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/hazel_1130/post/80614dea-9353-4782-8f00-621deef84905/image.png" alt=""></p>
<p>홈페이지 기본 화면</p>
<p><img src="https://velog.velcdn.com/images/hazel_1130/post/eba00acc-3fa8-4639-a218-fe3381836142/image.png" alt=""></p>
<p>로그인 화면</p>
<p><img src="blob:https://velog.io/f45d5f19-947d-4494-845d-ec8bbbdac227" alt="업로드중.."></p>
<p>사이드바를 통한 페이지 이동</p>
<pre><code class="language-ts">import { BrowserRouter as Router, Route, Routes } from &quot;react-router-dom&quot;;
import &quot;./App.css&quot;;

import Layout from &quot;./layout/Layout&quot;;import 
LoginLayout from &quot;./layout/LoginLayout&quot;;
import Home from &quot;./pages/Home&quot;;
import LoginPage from &quot;./pages/LoginPage&quot;;
import PageA from &quot;./pages/PageA&quot;;
import PageB from &quot;./pages/PageB&quot;;
import PageC from &quot;./pages/PageC&quot;;

function App() {
  return (
    &lt;Router&gt;
      &lt;Routes&gt;
        &lt;Route element={&lt;Layout /&gt;}&gt;
          &lt;Route path=&quot;/&quot; element={&lt;Home /&gt;} /&gt;
          &lt;Route path=&quot;/pageA&quot; element={&lt;PageA /&gt;} /&gt;
          &lt;Route path=&quot;/pageB&quot; element={&lt;PageB /&gt;} /&gt;
          &lt;Route path=&quot;/pageC&quot; element={&lt;PageC /&gt;} /&gt;
        &lt;/Route&gt;
        &lt;Route element={&lt;LoginLayout /&gt;}&gt;
          &lt;Route path=&quot;/login&quot; element={&lt;LoginPage /&gt;} /&gt;
        &lt;/Route&gt;
      &lt;/Routes&gt;
    &lt;/Router&gt;
  );
}

export default App;

</code></pre>
<p>Outlet을 이용한 중첩 레이아웃 구현</p>
<p><img src="https://velog.velcdn.com/images/hazel_1130/post/8acbe716-9af9-49e1-b0bd-4ea014419683/image.png" alt=""></p>
<p>페이지 이동은 React-Router-Dom을 이용한 라우팅을 했으며, React-Router-Dom에 있는 &#39;Outlet&#39;을 사용하여 레이아웃을 구성했다. 로그인 페이지는 헤더와 사이드를 적용시키지 말아달라는 조건이 있었기 때문이다.</p>
<p>페이지별 로그인 여부는 [IsLogin, SetLogin]= useState(false)와 useEffeect를 사용한 방법과 react-cookie 둘 중 어떤 방법이 더 효율적인지 고민 중이다. </p>
<p>3월에는 레이아웃과 로그인에 관한 커리큘럼을 진행한다고 한다. 챌린지를 진행하는 동안 해당 사전 과제를 어떻게 하면 더 깔끔하고 유지보수성이 좋게 짤 수 있는지 고민해볼 수 있는 좋은 기회라고 생각한다. 현재 프로젝트도 같이 하고 있지만 둘 다 잡을 수 있게 열심히 해야겠다. 😥</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[원티드 3월 프리온보딩 챌린지(1)]]></title>
            <link>https://velog.io/@hazel_1130/%EC%9B%90%ED%8B%B0%EB%93%9C-3%EC%9B%94-%ED%94%84%EB%A6%AC%EC%98%A8%EB%B3%B4%EB%94%A9-%EC%B1%8C%EB%A6%B0%EC%A7%801</link>
            <guid>https://velog.io/@hazel_1130/%EC%9B%90%ED%8B%B0%EB%93%9C-3%EC%9B%94-%ED%94%84%EB%A6%AC%EC%98%A8%EB%B3%B4%EB%94%A9-%EC%B1%8C%EB%A6%B0%EC%A7%801</guid>
            <pubDate>Mon, 27 Feb 2023 10:31:04 GMT</pubDate>
            <description><![CDATA[<p>개발자로서의 역량 향상을 위해 신청해본 원티드의 프리온보딩 챌린지! 취업을 준비하면서 아직은 내가 많이 부족하다는 생각이 들었고 이론보다는 실전을 배우고 싶어서 신청해보게 됐다!</p>
<h4 id="🚩챌린지-일정">🚩챌린지 일정</h4>
<p><img src="https://velog.velcdn.com/images/hazel_1130/post/3b51fe6b-6691-4ac6-b497-07603333bd47/image.png" alt="챌린지 일정"></p>
<h4 id="👩🏫커리큘럼">👩‍🏫커리큘럼</h4>
<p><img src="https://velog.velcdn.com/images/hazel_1130/post/b0532e13-b68c-41c3-aff1-67e63121459e/image.png" alt="사전 미션"></p>
<h4 id="🐹사전-미션-후기">🐹사전 미션 후기</h4>
<p>사전미션은 CSR 방식을 이용한 어플리케이션으로 3개 이상의 페이지를 가지도록 구현하는 것 + 공통으로 반복되는 헤더와 사이드바가 있도록 구성하는 것이다. <code>Typescript</code>와 <code>Tailwind</code>를 이용해서 과제를 작성했다. 기본적인 과제이니만큼 깔끔한 구성과 코드 작성을 하려고 노력했다.  </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Coddit] createAsyncThunk를 이용한 비동기 데이터 처리]]></title>
            <link>https://velog.io/@hazel_1130/Coddit-createAsyncThunk%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%B9%84%EB%8F%99%EA%B8%B0-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@hazel_1130/Coddit-createAsyncThunk%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-%EB%B9%84%EB%8F%99%EA%B8%B0-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Sun, 26 Feb 2023 11:33:16 GMT</pubDate>
            <description><![CDATA[<blockquote>
<h3 id="🤔왜-비동기-데이터-처리여야-했나">🤔왜 비동기 데이터 처리여야 했나?</h3>
</blockquote>
<p> Reddit 클론 프로젝트를 구현하는 데에 있어서 중요했던 것 중 하나는 동기식 및 비동기식 데이터 처리에 익숙해지는 것이었다.</p>
<p>  동기적 데이터 처리는 직관적이고 간단하며 프로그램이 선형 흐름을 따르기 때문에 실행 경로를 추적하고 발생할 수 있는 모든 오류를 정확하게 파악하는 것이 쉽다. 또한, 데이터 처리량이 상대적으로 적고 속도가 크게 중요하지 않은 소규모 응용 프로그램에 적합하다.</p>
<p>  그러나 동기식 데이터 처리에는 여러 가지 단점도 있는데, 가장 큰 단점 중 하나는 대량의 데이터를 처리할 때 느리고 비효율적일 수 있다는 것이다. 프로그램이 각 작업이 완료될 때까지 기다리기 때문에, 일련의 작업을 완료하는 데 시간이 오래 걸릴 수 있다. 이로 인해 사용자 경험에 부정적인 영향을 미칠 수 있기 때문에, 나는 더 나은 사용자 경험을 위해 이해하기 불편하고 어려워 보였던 비동기적 데이터 처리라는 불구덩이에 뛰어들기로 했다(...🔥😇) </p>
<p> 그리하여 내가 시도한 것은  <code>Redux Toolkit</code>의 <code>createAsyncThunk</code> 함수를 사용하여 비동기 데이터를 처리하는 방식이었다.<code>createAsyncThunk</code>를 사용한 예시 중 하나는 사용자가 본 게시물에 대한 데이터를 가져오는 것이다.</p>
<blockquote>
<h4 id="예시-코드-1">예시 코드 1</h4>
</blockquote>
<p> 해당 함수는 액션 유형을 나타내는 문자열과 데이터를 반환하는 비동기 함수 두 가지 인수를 취한다.</p>
<pre><code class="language-js">const getCurrentPost = createAsyncThunk(
  &quot;currentPost/getCurrentPost&quot;,
  async (postid) =&gt; {
    const resp = await axios.get(&quot;/api/currpost&quot;, {
      params: { postid: postid },
    });
    return resp.data;
  }
);

const currentPost = createSlice({
  name: &quot;currentPost&quot;,
  initialState: {
    list: [],
  },
  extraReducers: (builder) =&gt; {
    builder.addCase(getCurrentPost.pending, (state, action) =&gt; {
    });
    builder.addCase(getCurrentPost.fulfilled, (state, action) =&gt; {
      state.list = action.payload;
    });
    builder.addCase(getCurrentPost.rejected, (state, action) =&gt; {
    });
  },
});</code></pre>
<p>getCurrentPost 함수는 <code>createAsyncThunk</code> 를 사용하여 생성되며, postid를 매개변수로 사용하는 <code>dispatch</code> 메서드를 가지고 있다. 함수는 <code>axios</code> 라이브러리를 사용하여 postid 매개변수에 해당하는 서버 측 라우트에 대한 비동기 GET 요청을 수행합니다. 응답이 수신되면 함수는 데이터를 반환한다.</p>
<p>그 다음, getCurrentPost 함수를 사용하여 currentPost라는 상태 슬라이스를 만든다.  <code>extraReducers</code> 속성을 사용하여 대기 중(<code>pending</code>), 완료(<code>fulfilled</code>) 및 거부(<code>rejected</code>)와 같은 비동기 호출의 다양한 상태를 처리했다. 대기 중(<code>pending</code>) 상태가 트리거되면 상태에 아무 작업도 수행하지 않고, 완료(<code>fulfilled</code>) 상태가 트리거되면 액션 페이로드(<code>action.payload</code>)인 비동기 호출의 응답 데이터를 상태의 list 속성으로 설정했다.</p>
<blockquote>
<h4 id="예시-코드-2">예시 코드 2</h4>
</blockquote>
<p>개별 게시물 데이터를 가져오는 것 외에도, 사이드메뉴에서 토픽 및 서브토픽 데이터를 검색하는 기능도 구현해야 했는데, 게시물과 마찬가지로 extraReducers 속성을 사용하여 비동기 호출의 다른 상태를 처리했다. 아래 코드가 해당 구현 코드다.</p>
<pre><code class="language-js">const asyncTopic = createAsyncThunk(&quot;communitySlice/asyncTopic&quot;, async () =&gt; {
  const resp = await axios.get(&quot;/api/topic&quot;);
  return resp.data;
});

const topicSlice = createSlice({
  name: &quot;topicSlice&quot;,
  initialState: {
    list: [],
  },
  extraReducers: (builder) =&gt; {
    builder.addCase(asyncTopic.pending, (state, action) =&gt; {
      // console.log(&quot;pending&quot;);
    });
    builder.addCase(asyncTopic.fulfilled, (state, action) =&gt; {
      state.list = action.payload;
      // console.log(&quot;fulfilled&quot;);
    });
    builder.addCase(asyncTopic.rejected, (state, action) =&gt; {
      // console.log(&quot;rejected&quot;);
    });
  },
});

const asyncSub = createAsyncThunk(&quot;subSlice/asyncSub&quot;, async () =&gt; {
  const resp = await axios.get(&quot;/api/sub&quot;);
  return resp.data;
});

const subSlice = createSlice({
  name: &quot;subSlice&quot;,
  initialState: {
    list: [],
  },
  extraReducers: (builder) =&gt; {
    builder.addCase(asyncSub.pending, (state, action) =&gt; {
      // console.log(&quot;pending&quot;);
    });
    builder.addCase(asyncSub.fulfilled, (state, action) =&gt; {
      state.list = action.payload;
      // console.log(&quot;fulfilled&quot;);
    });
    builder.addCase(asyncSub.rejected, (state, action) =&gt; {
      // console.log(&quot;rejected&quot;);
    });
  },
});</code></pre>
<p> <code>Redux Toolkit</code>의 <code>createAsyncThunk</code> 함수를 사용하면 비동기 데이터 처리 과정을 단순화할 수 있다. 여러 상태에 대한 복수의 리듀서를 작성하지 않고도 비동기 작업을 생성할 수 있기 때문이다.</p>
<p> 해당 기능을 구현하느라 씨름을 해야했지만, 비동기 데이터를 처리하는 좋은 방법에 대해 배우고 익힐 수 있었다는 점에서 만족스러운 경험이었다. 또한 비동기와 동기, 두가지 데이터 처리 방식에 대해 알아보고, 또 장단점과 차이점, 어느 곳에 적합한지 연구하며 개발자로서 더 나은 사용자 경험에 대해 생각해볼 수 있는 시간이었다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Next.js 13] Error: Functions cannot be passed directly to Client Components because they're not serializable.]]></title>
            <link>https://velog.io/@hazel_1130/Error-Functions-cannot-be-passed-directly-to-Client-Components-because-theyre-not-serializable</link>
            <guid>https://velog.io/@hazel_1130/Error-Functions-cannot-be-passed-directly-to-Client-Components-because-theyre-not-serializable</guid>
            <pubDate>Thu, 23 Feb 2023 13:23:00 GMT</pubDate>
            <description><![CDATA[<p>Next.js 파일 디렉토리 공부를 하며 이것저것 만들어보다가 </p>
<blockquote>
<p>Error: Functions cannot be passed directly to Client Components because they&#39;re not serializable.</p>
</blockquote>
<p>이런 에러를 만나고 말았다. 코드 상에는 아무런 문제가 없었는데도
<img src="https://velog.velcdn.com/images/hazel_1130/post/4d3f0510-e35f-405f-82cc-3366806d7fd2/image.png" alt=""></p>
<p>화면에는 아무것도 안나오기 시작...
구글링 해보니 새로운 next.js 13에서 발생하는 오류라고 하며, error.jsx 파일이 오류를 발생시키는 원인이었다. 파일을 삭제하니 정상 동작하는 것을 발견...</p>
<p><img src="https://velog.velcdn.com/images/hazel_1130/post/bd12a130-8ffd-4c51-870d-0936b06a5a97/image.png" alt=""></p>
<p>아직은 Next.js 13이 완전히 안정화되지 않은 모양이다. 계속 이것저것 건드리고 만들어보면서 파악해나가는 것이 중요할 것 같다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next.js의 SSR 삼형제]]></title>
            <link>https://velog.io/@hazel_1130/Next.js%EC%9D%98-SSR-%EC%82%BC%ED%98%95%EC%A0%9C</link>
            <guid>https://velog.io/@hazel_1130/Next.js%EC%9D%98-SSR-%EC%82%BC%ED%98%95%EC%A0%9C</guid>
            <pubDate>Wed, 22 Feb 2023 16:25:37 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/hazel_1130/post/25300391-4fd2-46b8-8af9-0598123d3dd0/image.jpg" alt=""></p>
<blockquote>
<p>우와! Next.js의 SSR 삼형제다!!!</p>
</blockquote>
<h2 id="getserversideprops">getServerSideProps</h2>
<p>getServerSideProps란 Next.js에서 제공하는 라이프사이클 메서드 중 하나다. 이 메서드는 서버 사이드 렌더링(SSR)을 위해 사용된다. 기본적으로 브라우저에서 호출되는 일반적인 Next.js 페이지와 달리, getServerSideProps는 페이지 요청이 서버에 도달할 때마다 실행된다. 즉, 페이지를 렌더링하기 전에 데이터를 먼저 가져와서 페이지에 전달해주는 역할을 한다.</p>
<p>getServerSideProps는 서버에서 실행되는 JavaScript 코드를 포함할 수 있으며, 데이터를 fetch()하는 것과 같은 비동기 로직을 처리할 수 있다. 이 메서드는 서버에서만 실행되기 때문에, 클라이언트 측의 API 키와 같은 보안 정보를 노출하지 않도록 조심해야 한다.</p>
<p>다음은 getServerSideProps를 사용하여 데이터를 가져오고, 해당 데이터를 페이지에서 사용하는 간단한 예제이다.</p>
<pre><code class="language-js">import React from &quot;react&quot;;

export default function MyPage({ data }) {
  return (
    &lt;div&gt;
      &lt;h1&gt;My Page&lt;/h1&gt;
      &lt;ul&gt;
        {data.map((item) =&gt; (
          &lt;li key={item.id}&gt;{item.title}&lt;/li&gt;
        ))}
      &lt;/ul&gt;
    &lt;/div&gt;
  );
}

export async function getServerSideProps() {
  const res = await fetch(&quot;https://myapi.com/data&quot;);
  const data = await res.json();

  return {
    props: {
      data,
    },
  };
}</code></pre>
<p>위 예제에서, getServerSideProps 메서드는 서버에서 데이터를 가져온다. 해당 데이터는 MyPage 컴포넌트의 props로 전달된다.</p>
<p>또한 getServerSideProps는 라우트 정보를 가져오는 params와 query를 인자로 받을 수 있으며, 이를 통해 동적인 페이지를 만들 수 있다. </p>
<p>이렇게 서버에서 데이터를 가져오면, 페이지를 렌더링하기 전에 데이터를 미리 채울 수 있으므로 사용자가 페이지를 볼 때까지 대기 시간을 최소화할 수 있으며, 이를 통해 사용자 경험을 개선하고, 검색 엔진 최적화(SEO)를 향상시킬 수 있다.</p>
<h2 id="getstaticprops">getStaticProps</h2>
<p>getStaticProps는 Next.js에서 제공하는 pre-rendering(사전 렌더링) 방식 중 하나로, 빌드 시점에서 정적인 데이터를 미리 가져와서 HTML을 생성하는 방법이다. 이를 통해, 빌드한 후에는 서버에서 데이터를 가져올 필요 없이, 정적인 페이지를 빠르게 제공할 수 있다. </p>
<pre><code class="language-js">export async function getStaticProps() {
  const data = await fetchData(); // 데이터 가져오기
  return {
    props: {
      data, // props로 전달할 데이터
    },
  };
}</code></pre>
<p>getStaticProps는 페이지 컴포넌트에서만 사용 가능하다. 해당 페이지 컴포넌트에서 사용할 데이터를 미리 가져오는 함수를 정의해주면 되는데 이 함수는 Next.js에서 정해진 규칙에 따라 정의되어야 한다. 정의된 함수는 서버에서 실행되며, 반환된 데이터는 페이지 컴포넌트의 props로 전달된다.</p>
<p>아래는 getStaticProps를 사용하여 블로그 글 목록 페이지를 구현한 예시다.</p>
<pre><code class="language-js">import Link from &quot;next/link&quot;;
import { getSortedPostsData } from &quot;../lib/posts&quot;;

export default function Blog({ posts }) {
  return (
    &lt;ul&gt;
      {posts.map((post) =&gt; (
        &lt;li key={post.id}&gt;
          &lt;Link href={`/posts/${post.id}`}&gt;
            &lt;a&gt;{post.title}&lt;/a&gt;
          &lt;/Link&gt;
        &lt;/li&gt;
      ))}
    &lt;/ul&gt;
  );
}

export async function getStaticProps() {
  const posts = getSortedPostsData();
  return {
    props: {
      posts,
    },
  };
}</code></pre>
<p>해당 코드에서 getStaticProps 함수는 getSortedPostsData 함수를 호출하여 블로그 포스트 데이터를 가져오고, posts라는 props에 해당 데이터를 담아 리턴한다. 리턴된 posts는 Blog 컴포넌트에서 props로 전달되어 map 함수를 이용해 블로그 포스트 목록을 렌더링하게 된다. </p>
<blockquote>
<p>주의사항
getStaticProps를 사용하려면 반드시 정적인 데이터를 사용해야 한다. 즉, 요청 시점에 데이터가 변경될 가능성이 없는 경우에만 사용할 수 있다. 이를 통해 빌드 시점에 페이지를 생성하여, 요청 시점에 서버에서 데이터를 가져올 필요 없이, 빠르게 페이지를 제공할 수 있다.</p>
</blockquote>
<h2 id="getstaticpaths">getStaticPaths</h2>
<p>getStaticPath는 동적 경로를 가지는 페이지에서 사용된다. 일반적인 동적 페이지 라우팅 시스템과는 달리, Next.js는 미리 생성된 정적 파일을 제공하므로 앱의 성능을 향상시킬 수 있다. 해당 함수를 사용하면 동적 경로를 가지는 페이지의 가능한 경로들을 미리 정의할 수 있으며, 이렇게 하면 미리 빌드된 정적 파일을 생성하여 페이지 로드 시간을 크게 줄일 수 있다는 장점이 있다.</p>
<pre><code class="language-js">// pages/posts/[id].js

import { getPostData, getAllPostIds } from &#39;../../lib/posts&#39;

export default function Post({ postData }) {
  return (
    &lt;div&gt;
      &lt;h1&gt;{postData.title}&lt;/h1&gt;
      &lt;br /&gt;
      &lt;p&gt;{postData.content}&lt;/p&gt;
    &lt;/div&gt;
  )
}

export async function getStaticPaths() {
  const paths = getAllPostIds()
  return {
    paths,
    fallback: false
  }
}

export async function getStaticProps({ params }) {
  const postData = getPostData(params.id)
  return {
    props: {
      postData
    }
  }
}</code></pre>
<p>위의 예시에서 getAllPostIds 함수는 모든 게시물 ID를 배열로 반환한다. getStaticPaths 함수는 이 배열을 paths 객체의 속성 값으로 사용한다. 이 과정에서 Next.js가 페이지 빌드 시 각 게시물에 대한 정적 파일을 생성하게 된다.</p>
<p>fallback 옵션은 모든 가능한 경로가 미리 정의되지 않은 경우 무엇을 해야 할 지를 결정하는 역할을 하는데, 이 값을 false로 설정하면 정의되지 않은 경로에 대해 404 오류가 반환되고, true로 설정하면 Next.js가 요청을 처리하며 생성된 정적 파일이 없는 경우 요청을 대체하는 동적 페이지를 생성한다. </p>
<h2 id="요약">요약</h2>
<p>getServerSideProps: 매 요청 시마다 실행되며 서버에서 데이터를 가져와 페이지를 렌더링. 동적인 데이터를 다룰 때 사용.</p>
<p>getStaticProps: 빌드 시점에 실행되며 데이터를 미리 가져와 정적인 페이지를 생성합. 정적인 페이지를 다룰 때 사용.</p>
<p>getStaticPaths: 빌드 시점에 실행되며 동적인 라우팅을 구현할 때 사용됩. 라우트의 경로를 동적으로 생성 가능.</p>
<p>이 세 가지 함수는 모두 Next.js에서 제공하는 페이지 프리렌더링 기능을 사용하여 페이지의 성능과 SEO 최적화를 개선할 수 있다. </p>
<p><strong>getServerSideProps</strong>는 매 요청마다 실행되기 때문에 렌더링 시간이 다소 느릴 수 있지만 항상 최신 데이터를 반영할 수 있다는 장점이 있으며 
<strong>getStaticProps</strong>와 <strong>getStaticPaths</strong>는 빌드 시점에 실행되기 때문에 렌더링 속도가 빠르고 캐싱이 용이하다는 장점이 있다. </p>
<p>정적인 페이지를 다룰 때는 <strong>getStaticProps</strong>를 사용하고, 동적인 페이지를 다룰 때는 <strong>getServerSideProps</strong>를 사용하며, 동적인 라우팅을 구현할 때는 <strong>getStaticPaths</strong>와 함께 사용한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[모행?! 프로젝트 기획 배경 및 기능 정의]]></title>
            <link>https://velog.io/@hazel_1130/%EB%AA%A8%ED%96%89-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B8%B0%ED%9A%8D-%EB%B0%B0%EA%B2%BD-%EB%B0%8F-%EA%B8%B0%EB%8A%A5-%EC%A0%95%EC%9D%98</link>
            <guid>https://velog.io/@hazel_1130/%EB%AA%A8%ED%96%89-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%EA%B8%B0%ED%9A%8D-%EB%B0%B0%EA%B2%BD-%EB%B0%8F-%EA%B8%B0%EB%8A%A5-%EC%A0%95%EC%9D%98</guid>
            <pubDate>Tue, 14 Feb 2023 15:59:28 GMT</pubDate>
            <description><![CDATA[<h2 id="🔎프로젝트-배경">🔎프로젝트 배경</h2>
<hr>
<p>끝날 듯 끝나지 않는 언택트의 시대, 좋은 프로젝트를 만들고 싶다는 목표를 가지고 모두의 여행은 시작됐다. 내가 들어왔을 때에는 이미 프로젝트가 일주일 정도 진행된 시점이었기 때문에 프로젝트의 아이템이 정해진 상태였다. 여행 커뮤니티. 사실 그렇게 특별한 프로젝트 아이템은 아니다. 하지만 좋은 결과물은 항상 약간의 변주에서 나온다. 모두의 여행도 거기에서 시작됐다.</p>
<p>우선 흔히들 떠올리는 여행 플랫폼이 필요한 이유를 살펴보자. 기존 여행 플랫폼이 제공하는 서비스는 여행지에 대한 정보와 예약 서비스 제공이 있다. 그러나 현재 주요 여행 소비층이 그런 서비스를 많이 소비하고 필요로 한다고 말할 수 있을까? 여행 관련 정보를 검색하고자 하는 사용자들은 많이 있지만 개인 블로그나 여행 후기 사이트 등은 검색하기 번거롭고 광고성 글이 많기에 정확한 정보를 찾기 어렵다. </p>
<p><strong>그렇다면  여행 플랫폼이지만 타 여행 플랫폼와 차별점을 줄만한 요소가 뭐가 있을까?</strong> </p>
<h2 id="📚프로젝트-목표와-기획-의도">📚프로젝트 목표와 기획 의도</h2>
<hr>
<p><img src="https://velog.velcdn.com/images/hazel_1130/post/f0e31248-a120-4951-bff6-35fb05c21a72/image.png" alt=""></p>
<p>사용자가 만들어가는 여행 커뮤니티, 우리가 정한 프로젝트의 목표는 다음과 같았다.</p>
<p><strong>사용자가 직접 일정을 작성하고 공유해 경험과 지식을 나눔으로써 서로 정보를 주고 받을 수 있는 커뮤니티형 플랫폼을 만들자.</strong></p>
<h2 id="📑기능-정의">📑기능 정의</h2>
<hr>
<p><img src="https://velog.velcdn.com/images/hazel_1130/post/d9c584ad-8ec6-4bb8-a6ea-4e3822fe0741/image.png" alt="">
<img src="https://velog.velcdn.com/images/hazel_1130/post/cdc71575-34a9-446e-ad6f-59ab8dc8c38f/image.png" alt=""></p>
<h2 id="✔주요-기능">✔주요 기능</h2>
<hr>
<p>결과적으로 구현하게 된 주요 기능은 다음과 같다. </p>
<ul>
<li>로그인, 회원가입 기능</li>
<li>마이페이지<ul>
<li>북마크 : 유저가 북마크한 여행지와 코스를 확인할 수 있으며 북마크를 비활성화 할 수 있는 기능</li>
<li>내가 쓴 리뷰 : 유저가 작성한 리뷰를 확인할 수 있으며 수정, 삭제가 가능한 기능</li>
<li>나의 여행 일정 : 유저가 작성한 코스를 확인 할 수 있으며 해당 코스의 공개/비공개 여부를 변경 가능, 수정 삭제 가능한 기능</li>
</ul>
</li>
<li>여행지<ul>
<li>해당 여행지에 대한 상세 정보를 확인할 수 있는 기능</li>
<li>유저가 원하는 여행지를 즐겨찾기 할 수 있는 기능</li>
<li>상세 페이지에서 별점과 리뷰를 줄 수 있는 기능</li>
<li>평균 별점 순으로 정렬해서 인기가 높은 여행지를 우선적으로 볼 수 있는 기능</li>
</ul>
</li>
<li>코스<ul>
<li>다른 유저와 본인이 작성한 코스의 상세 정보를 확인할 수 있는 기능</li>
<li>좋아요 버튼을 통해 인기 많은 코스 우선으로 정렬 가능</li>
<li>유저가 원하는 코스를 즐겨찾기 할 수 있는 기능</li>
<li>원하는 코스를 공유할 수 있는 기능</li>
<li>맵 버튼을 통해 상세페이지를 들어가지 않고도 코스의 경로를 간단히 확인할 수 있는 기능</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Next.js는 뭐고 13버전에서는 뭐가 달라졌을까?]]></title>
            <link>https://velog.io/@hazel_1130/Next.js%EB%8A%94-%EB%AD%90%EA%B3%A0-13%EB%B2%84%EC%A0%84%EC%97%90%EC%84%9C%EB%8A%94-%EB%AD%90%EA%B0%80-%EB%8B%AC%EB%9D%BC%EC%A1%8C%EC%9D%84%EA%B9%8C</link>
            <guid>https://velog.io/@hazel_1130/Next.js%EB%8A%94-%EB%AD%90%EA%B3%A0-13%EB%B2%84%EC%A0%84%EC%97%90%EC%84%9C%EB%8A%94-%EB%AD%90%EA%B0%80-%EB%8B%AC%EB%9D%BC%EC%A1%8C%EC%9D%84%EA%B9%8C</guid>
            <pubDate>Tue, 14 Feb 2023 15:34:43 GMT</pubDate>
            <description><![CDATA[<h2 id="nextjs란">Next.js란?</h2>
<p>Next.js는 React 기반의 서버사이드 렌더링 프레임워크이다. React 프로젝트에서 서버사이드 렌더링을 구현하기 위해서는 많은 설정과 작업이 필요하지만, Next.js를 사용하면 이러한 작업들을 간단하게 처리할 수 있기 때문에 요즘 많이 사용하는 추세다.</p>
<p>Next.js의 장점이라고 한다면</p>
<p>높은 초기 렌더링 성능
자동으로 처리되는 코드 스플리팅과 프리렌더링
파일 시스템 기반의 라우팅을 제공
API 라우팅과 같은 기능을 포함한다는 점일 것이다.</p>
<p>Next.js의 단점은 </p>
<p>세부적인 설정을 제어하기가 어렵고
상대적으로 배우기 어려우며
일부 적용되지 않는 React 플러그인이 있을 수 있다는 점인데
단점보다는 장점이 독보적인지라 나도 이번 프로젝트에서 
눈 딱감고 낙하하는 심정으로 Next.js를 써보기로 마음 먹었다.</p>
<h2 id="nextjs-13-뭐가-달라졌을까">Next.js 13 뭐가 달라졌을까?</h2>
<p>Next.js 13 버전은 이전 버전보다 많은 새로운 기능과 개선된 성능을 제공한다.</p>
<h4 id="image-컴포넌트">Image 컴포넌트</h4>
<p>우선 Image 컴포넌트의 성능이 향상되었다. 
새로운 blurDataURL 옵션을 사용하여 이미지를 미리 로드하고 블러 처리된 이미지를 보여줌으로써, 이미지 로딩이 완료될 때까지 사용자는 빈 화면이 아닌 블러 처리된 이미지를 볼 수 있다.</p>
<h4 id="script-컴포넌트">Script 컴포넌트</h4>
<p>새로운 Script 컴포넌트가 추가되었다. Script 컴포넌트를 사용하여 클라이언트 측에서 자바스크립트 코드를 로드하고 실행할 수 있게 됐다. 또한, Script 컴포넌트는 Google Analytics와 같은 서비스를 쉽게 추가할 수 있도록 지원한다.</p>
<h4 id="간편한-css-모듈-지원">간편한 CSS 모듈 지원</h4>
<p>더욱 간편한 CSS 모듈 지원이 추가됐다. 이전 버전에서는 CSS 모듈을 사용할 때 styles.module.css와 같은 파일명을 사용해야 했지만, Next.js 13에서는 .module.css 확장자를 사용하는 것으로 충분하다. (만세!)</p>
<h4 id="기타">기타</h4>
<p><strong>Live Preview</strong>
변경 사항이 있을 때마다 자동으로 페이지를 빌드하고 브라우저에서 미리보기할 수 있도록 지원하는 Live Preview 기능</p>
<p><strong>apiRoutes</strong>
자동으로 생성된 OpenAPI 스키마를 사용하여 API 라우트를 생성할 수 있는 새로운 apiRoutes 기능</p>
<p><strong>useSession</strong>
 Next.js 내부에서 쉽게 세션 관리를 할 수 있도록 지원하는 useSession 훅 기능, 이를 통해 로그인, 로그아웃 등의 인증 처리를 더욱 간편하게 할 수 있다.</p>
<p>이 외에도 다국어 지원을 해주는 localization 기능 등 Next.js 13에서는 다양한 개선사항과 새로운 기능이 추가되어 있다. 아직은 공식 문서를 통한 숙지가 더 필요해보인다. </p>
<p><a href="https://nextjs.org/blog/next-13">Next.js 13 공식문서 바로가기</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Java 07 메소드]]></title>
            <link>https://velog.io/@hazel_1130/Java-07-%EB%A9%94%EC%86%8C%EB%93%9C</link>
            <guid>https://velog.io/@hazel_1130/Java-07-%EB%A9%94%EC%86%8C%EB%93%9C</guid>
            <pubDate>Fri, 27 May 2022 07:27:18 GMT</pubDate>
            <description><![CDATA[<h3 id="메소드란">메소드란?</h3>
<p>메소드란 프로그램을 실행하게 하는 동작(처리)의 집합을 기술한 것, 또는 그 동작의 집합을 호출하는 기술 부분을 뜻한다. 
Java에는 여러가지 동작을 실시하는 메소드가 이미 준비되어있고, 그중에는 우리가 이미 자주 사용하고 있는 메소드들도 있다. (ex : println, print 메소드/ nextInt 메소드 등) 
메소드의 동작은 호출한 시점에서 자동으로 동작하므로 내부 처리가 어떻게 기술되어있는지 모르더라도 간단히 이용이 가능하다.</p>
<p>이러한 Java의 기존의 메소드는, Java API(Application Program Interface)라고 불리는 문서에 정리되고 있어 web에서 확인 가능하다.</p>
<h3 id="메소드-정의-및-호출">메소드 정의 및 호출</h3>
<p>메소드의 기본 구문은 아래와 같다.</p>
<p>&lt;기본구문&gt;</p>
<blockquote>
</blockquote>
<pre><code>static 수식자 리턴형 메소드명(인수) {
    처리내용;
    return 문;
}</code></pre><p>&lt;선언된 메소드 호출시&gt;</p>
<blockquote>
</blockquote>
<pre><code>메소드명(인수);</code></pre><p>&lt;예제&gt;</p>
<pre><code>public class MyAdditionMethod {

    static int additionMethod(int a, int b){
        int sum = a + b;
        return sum;
    }

    public static void main(String[] args) {

        // 계산용 값을 관리하는 변수 선언 및 초기화 
        int a = 5;
        int b = 10;

        int answer = additionMethod(a, b);

        System.out.println(&quot;a = &quot; + a);
        System.out.println(&quot;b = &quot; + b);
        System.out.println(&quot;a + b = &quot; + answer);

    }
}</code></pre><p>해당 메소드는 변수 a와 b를 더해 그 계산결과를 돌려주는 메소드다.
위의 기본 구문과 비교 및 대입하여 생각해보면 메소드의 구성이 어떻게 되고 실제 코드 작성시 어떻게 이용되는지 파악할 수 있다.</p>
<h3 id="메소드-정의">메소드 정의</h3>
<blockquote>
<p>&lt;메소드 정의 서식&gt;</p>
</blockquote>
<pre><code>static 수식자① 리턴형② 메소드명③(인수④) {
    처리내용⑤;
    return 문⑥;
}</code></pre><h4 id="👉static-수식자">👉static 수식자</h4>
<p>해당 수식자는 앞으로 정의하는 메소드를 어떤 범위나 형태로 이용할 수 있을지에 대한 것이다. 구성원 변수와 구성원 방법을 수식하는데 이용된다. </p>
<h4 id="👉return문과-return값">👉return문과 return값</h4>
<p>리턴값(반환값)은 해당 메소드 처리 결과값이다. 메소드 호출원의 처리로 이용된다. 리턴값을 가지는 메소드의 경우는 반드시 리턴문으로 선언하고 있던 형태를 리턴값으로 돌려주지 않으면 안되며 하나의 변수만 반환이 가능하다.</p>
<h4 id="👉메소드명">👉메소드명</h4>
<p> 메소드명은 메소드에 붙인 이름이다. 소문자로 시작해 두번째 이후의 단어 첫글자를 대문자로 하는 카멜 케이스가 관례다.</p>
<h4 id="👉인수">👉인수</h4>
<p>인수는 메소드를 호출할 때 그 메소드에 건네주는 값이다. 
정의 부분에 기술하는 인수를 가인수라고 하며 메소드 내의 처리로 이용된다.
메소드명 뒤의 괄호에는 필요한 인수의 형태와 변수명을 기술해야만 한다.
인수를 복수로 받는 메소드의 경우에는 콤마( , )를 이용하여 단락 짓는다. 
또한 인수를 받지 않는 경우에는 괄호 안에 기술하지 않는다.</p>
<p>메소드를 호출할 때 기술하는 인수는 실인수라고 칭한다. 실인수는 메소드의 선언부분에 쓰여진 인수의 형태, 기술한 순서, 개수에 일치시킬 필요가 있다.</p>
<p>⭐포인트
가인수와 실인수는 인수의 형태, 순서, 개수를 일치시킨다.
인수가 복수 있는 경우는 ,(쉼표)로 단락짓는다. 인수가 없는 경우는, ( ) 안에 아무것도 쓰지 않는다.</p>
<h4 id="👉처리">👉처리</h4>
<p>인수와 리턴값과 메서드의 조합 패턴은 크게 나누어 다음 네가지 패턴이 된다.
1)인수 있음, 리턴 값 있음
2)인수 있음, 리턴 값 없음
3)인수 없음, 리턴 값 있음
4)인수 없음, 리턴 값 없음</p>
<h3 id="메소드-정의-위치">메소드 정의 위치</h3>
<p>메소드를 정의하는 장소는 main 메소드 전후 어느 쪽이어도 괜찮지만, 어떠한 클래스 내에 있어야 하며 메소드끼리 충첩되어서는 안된다.</p>
<h3 id="메소드의-편리성">메소드의 편리성</h3>
<p>메소드를 부품처럼 적재적소에 사용하면 쾌적한 코드를 만들 수 있다.</p>
<blockquote>
<p>&lt;예시 1 메소드를 사용하지 않는 경우&gt;</p>
</blockquote>
<pre><code>public class NoMethod {

    public static void main(String[] args) {

        // 점수를 관리하는 변수 선언 및 초기화
        int pointA = 85;
        int pointB = 32;
        int pointC = 60;
        int pointD = 40;

        System.out.print(&quot;A남&quot;);
        if (pointA &gt;= 50) {
            System.out.println(pointA + &quot;점으로 합격!&quot;);
        } else {
            System.out.println(pointA + &quot;점으로 불합격!&quot;);
        }
        System.out.println(&quot; ======= &quot;);

        System.out.print(&quot;B님&quot;);
        if (pointB &gt;= 50) {
            System.out.println(pointB + &quot;점으로 합격!&quot;);
        } else {
            System.out.println(pointB + &quot;점으로 불합격!&quot;);
        }
        System.out.println(&quot; ======= &quot;);

        System.out.print(&quot;C님&quot;);
        if (pointC &gt;= 50) {
            System.out.println(pointC + &quot;점으로 합격!&quot;);
        } else {
            System.out.println(pointC + &quot;점으로 불합격!&quot;);
        }

        System.out.println(&quot; ======= &quot;);

        System.out.print(&quot;D님&quot;);

        if (pointD &gt;= 50) {
            System.out.println(pointD + &quot;점으로 합격!&quot;);

        } else {
            System.out.println(pointD + &quot;님으로 불합격!&quot;);
        }
        System.out.println(&quot; ======= &quot;);

    }
}</code></pre><blockquote>
<p>&lt;예시 2 메소드를 사용하는 경우&gt;</p>
</blockquote>
<pre><code>public class Method {

    static void checkPass(int point){

        if(point &gt;= 50){
            System.out.println(point + &quot;점으로 합격!&quot;);
        }else{
            System.out.println(point + &quot;점으로 불합격!&quot;);
        }
        System.out.println(&quot; ======= &quot;);
    }

    public static void main(String[] args) {

        // 점수를 관리하는 변수 및 초기화
        int pointA = 85;
        int pointB = 32;
        int pointC = 60;
        int pointD = 40;

        System.out.print(&quot;A님, &quot;);
        checkPass(pointA);

        System.out.print(&quot;B님, &quot;);
        checkPass(pointB);

        System.out.print(&quot;C님, &quot;);
        checkPass(pointC);

        System.out.print(&quot;D님, &quot;);
        checkPass(pointD);
    }
}</code></pre><p>if문을 사용한 checkpass 메소드를 만들었을 때 같은 결과를 도출하는 코드라도
훨씬 가독성이 좋고 깔끔한 코드가 완성되는 것을 볼 수 있다. 또한 메소드를 이용하게 되면 코드를 유지보수하기가 훨씬 쉬워진다.</p>
<p>아래 예시들은 기존 예시 1의 코드를 다양한 형태의 메소드를 사용해 작성한 예시들이다. </p>
<blockquote>
<p>&lt;예시 3 같은 자료형의 인수를 가지는 메소드&gt;</p>
</blockquote>
<pre><code>public class TwoArgument {

    static void checkPass(int point, int passPoint) {

        if (point &gt;= passPoint) {
            System.out.println(point + &quot;점 합격!&quot;);
        } else {
            System.out.println(point + &quot;점 불합격!&quot;);
        }
        System.out.println(&quot; ======= &quot;);

    }

    public static void main(String[] args) {

        int pointA = 85;
        int pointB = 32;
        int pointC = 60;
        int pointD = 40;

        System.out.print(&quot;A님, &quot;);
        checkPass(pointA, 50);

        System.out.print(&quot;B님, &quot;);
        checkPass(pointB, 50);

        System.out.print(&quot;C님, &quot;);
        checkPass(pointC, 50);

        System.out.print(&quot;D님, &quot;);
        checkPass(pointD, 50);
    }
}</code></pre><blockquote>
<p>&lt;예시 4 여러 자료형의 인수를 가진 메소드&gt;</p>
</blockquote>
<pre><code>public class VariousArgument {

    static void checkPass(String name, int point, int passPoint) {

        System.out.print(name + &quot;님, &quot;);

        if (point &gt;= passPoint) {
            System.out.println(point + &quot;점 합격!&quot;);
        } else {
            System.out.println(point + &quot;점 불합격!&quot;);
        }
        System.out.println(&quot; ======== &quot;);
    }

    public static void main(String[] args) {

        int pointA = 85;
        int pointB = 32;
        int pointC = 60;
        int pointD = 40;

        checkPass(&quot;A&quot;, pointA, 50);

        checkPass(&quot;B&quot;, pointB, 50);

        checkPass(&quot;C&quot;, pointC, 50);

        checkPass(&quot;D&quot;, pointD, 50);

    }
}</code></pre><blockquote>
<p>&lt;예시 5배열의 요소를 인수에 받는 메소드&gt;</p>
</blockquote>
<pre><code>public class MethodWithArrayElement {

    static void checkPass(int point){

        if(point &gt;= 50){
            System.out.println(point + &quot;점 합격!&quot;);
        }else{
            System.out.println(point + &quot;점 불합격!&quot;);
        }
        System.out.println(&quot; ======== &quot;);

    }

    public static void main(String[] args) {

        // String형의 이름을 관리하는 배열의 선언과 초기화
        String[] aryName = {&quot;A&quot;, &quot;B&quot;, &quot;C&quot;, &quot;D&quot;};

        // int형 점수를 관리하는 배열의 선언과 초기화 
        int[] point = {85, 32, 60, 40};

        for(int i = 0; i &lt; aryName.length; i++){
            System.out.print(aryName[i] + &quot;님, &quot;);
            checkPass(point[i]);
        }
    }
}</code></pre><p>해당 코드는 main 메소드 위에 메소드 본체가 기술된다. 먼저 실행되는 것은 main메소드 안의 String형 배열 변수 aryName의 선언과 4개의 요소수 초기화다. 다음으로는 int형 배열 point의 선언과 4개의 요소수 초기화가 실행된다. 그다음 for문으로 &#39;i가 4보다 작다&#39;라는 의미를 가진 조건식이 실행된다. 그 뒤 checkpass메소드를 for문 안에 기술해 처리하면 된다. 해당 예제는 형식이 맞으면 배열 변수의 요소도 인수로 전달이 가능하다는 것을 보여준다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Java 06 배열]]></title>
            <link>https://velog.io/@hazel_1130/Java-06-%EB%B0%B0%EC%97%B4</link>
            <guid>https://velog.io/@hazel_1130/Java-06-%EB%B0%B0%EC%97%B4</guid>
            <pubDate>Fri, 13 May 2022 13:05:48 GMT</pubDate>
            <description><![CDATA[<h3 id="배열이란">배열이란?</h3>
<p>배열은 같은 형태의 변수를 하나로 정리한 것
앞으로 자바 프로그래밍에 있어서 잘 다뤄야하는 개념 중 하나이다
배열을 이용하면 코드가 훨씬 간략해지고 가독성이 자체도 올라가기 때문이다</p>
<h3 id="배열의-선언과-영역-확보">배열의 선언과 영역 확보</h3>
<p>배열의 선언은 간단하다</p>
<blockquote>
</blockquote>
<p>자료형[] 배열명; 
배열명 = new 자료형명[요소수];
ex &gt; 
int[] jasonAPT;
jasonAPT = new int[3];</p>
<p>배열명을 선언 뒤 new 연산자를 선언하면 된다.
하여 해당 배열변수는 값이 들어가 있는 위치를 참조하게 된다.
중요한 것은 배열의 시작은 항상 [0]이라는 것이다. 
첫번째 배열 요소수를 선언하고 싶을 경우에는 배열명[0]을 호출해줘야한다.
또한 배열의 길이를 바꾸고 싶은 경우는
일단 기억영역을 확보하면 변경할 수 없기에 새롭게 자료형명과 배열수를 선언해야 한다.</p>
<p>선언과 확보를 동시에 하는 것도 가능한데, 해당 선언은 이렇게 한다.</p>
<blockquote>
</blockquote>
<p>자료형명[] 객체명 = new 자료형[요소수]
ex &gt; 
int[] jasonAPT = new int[3];</p>
<h3 id="배열의-요소에-값의-대입">배열의 요소에 값의 대입</h3>
<blockquote>
</blockquote>
<p>배열명[요소수] = 값
ex &gt; 
jasonAPT[0] = 3;</p>
<h3 id="배열-초기화-방법">배열 초기화 방법</h3>
<blockquote>
</blockquote>
<p>ex&gt;
int[] jasonAPT = { 3, 1, 10 }</p>
<blockquote>
<p>int[] jasonAPT = new int[3];
jasonAPT[0] = 3;
jasonAPT[1] = 1;
jasonAPT[2] = 10;</p>
</blockquote>
<h3 id="배열-요소값-변경">배열 요소값 변경</h3>
<p>ex&gt;</p>
<pre><code>public class ChangeArrayElement1 {

    public static void main(String[] args) {

        // 주거년수를 관리하기 위한 배열변수 선언과 요소의 초기화
        int[] jasonAPT = { 3, 1, 10 };

        System.out.println(&quot;0호 거주년수 : &quot; + jasonAPT[0]);
        System.out.println(&quot;1호 거주년수 : &quot; + jasonAPT[1]);
        System.out.println(&quot;2호 거주년수 : &quot; + jasonAPT[2]);

        // 일부 배열요소에 값을 할당
        jasonAPT[1] = 2;

        System.out.println();
        System.out.println(&quot; - 변경후 거주년수 - &quot;);

        System.out.println(&quot;0호 거주년수 : &quot; + jasonAPT[0]);
        System.out.println(&quot;1호 거주년수 : &quot; + jasonAPT[1]);
        System.out.println(&quot;2포 거주년수 : &quot; + jasonAPT[2]);
    }
}</code></pre><h3 id="배열-변수끼리-대입">배열 변수끼리 대입</h3>
<p>변수와 마찬가지로 배열 변수 또한 다른 배열 변수에 할당할 수 있다.
다만 변수와 달리 배열은 그 배열 변수의 주소가 복사된다.</p>
<blockquote>
</blockquote>
<p>배열명 B = 배열명 A;
jasonAPTB = jasonAPTA</p>
<pre><code>public class ChangeArrayElement2 {

    public static void main(String[] args) {

        // 음료가격을 관리하는 배열 jasonPrice의 선언과 요소 초기화
        int[] jasonPrice = { 3000, 4000, 5000 };

        // 음료 가격을 관리하는 배열 gangnamPrice를 선언하고 배열 jasonPrice로 초기화
        int[] gangnamPrice = jasonPrice;

        System.out.println(&quot;===== 홍대점 ===== &quot;);
        System.out.println(&quot;차　　:&quot; + jasonPrice[0] + &quot;원&quot;);
        System.out.println(&quot;홍차　:&quot; + jasonPrice[1] + &quot;원&quot;);
        System.out.println(&quot;커피 :&quot; + jasonPrice[2] + &quot;원&quot;);

        System.out.println(&quot;===== 강남점 =====&quot;);
        System.out.println(&quot;차　　:&quot; + gangnamPrice[0] + &quot;원&quot;);
        System.out.println(&quot;홍차　:&quot; + gangnamPrice[1] + &quot;원&quot;);
        System.out.println(&quot;커피 :&quot; + gangnamPrice[2] + &quot;원&quot;);

        System.out.println();
        System.out.println(&quot;가격 변경 후&quot;);

        // 강남점의 차 가격 변경
        gangnamPrice[1] = 4500;

        System.out.println(&quot;===== 홍대점 =====&quot;);
        System.out.println(&quot;차 　:&quot; + jasonPrice[0] + &quot;원&quot;);
        System.out.println(&quot;홍차　:&quot; + jasonPrice[1] + &quot;원&quot;);
        System.out.println(&quot;커피 :&quot; + jasonPrice[2] + &quot;원&quot;);

        System.out.println(&quot;===== 강남점 =====&quot;);
        System.out.println(&quot;차　　:&quot; + gangnamPrice[0] + &quot;원&quot;);
        System.out.println(&quot;홍차　:&quot; + gangnamPrice[1] + &quot;원&quot;);
        System.out.println(&quot;커피 :&quot; + gangnamPrice[2] + &quot;원&quot;);
    }
}</code></pre><h3 id="배열의-요소를-복사하는-방법">배열의 요소를 복사하는 방법</h3>
<p>배열의 요소값을 공유하지 않고 복사하는 방법도 있다. 이를 위해서는 요소의 인덱스를 지정해야한다. </p>
<blockquote>
<p>배열명D[배열요소수] = 배열명C[배열요소수];
jasonAPTD[0] = jasonAPTC[0];</p>
</blockquote>
<pre><code>    public static void main(String[] args) {

        // 음료가격을 관리하는 배열 jasonPrice의 선언과 요소 초기화
        int[] jasonPrice = { 3000, 4000, 5000 };

        // 음료가격을 관리하는 배열 gangnamPrice를 선언하고 배열 jasonPrice의 각 요소로 초기화
        int[] gangnamPrice = new int[3];

        gangnamPrice[0] = jasonPrice[0];
        gangnamPrice[1] = jasonPrice[1];
        gangnamPrice[2] = jasonPrice[2];

        System.out.println(&quot;===== 홍대점 ===== &quot;);
        System.out.println(&quot;차　　:&quot; + jasonPrice[0] + &quot;원&quot;);
        System.out.println(&quot;홍차　:&quot; + jasonPrice[1] + &quot;원&quot;);
        System.out.println(&quot;커피 :&quot; + jasonPrice[2] + &quot;원&quot;);

        System.out.println(&quot;===== 강남점 =====&quot;);
        System.out.println(&quot;차　　:&quot; + gangnamPrice[0] + &quot;원&quot;);
        System.out.println(&quot;홍차　:&quot; + gangnamPrice[1] + &quot;원&quot;);
        System.out.println(&quot;커피 :&quot; + gangnamPrice[2] + &quot;원&quot;);

        System.out.println();
        System.out.println(&quot;가격변경 후&quot;);

        // 강남점의 차 가격 변경
        gangnamPrice[1] = 4500;

        System.out.println(&quot;===== 홍대점 =====&quot;);
        System.out.println(&quot;차 　:&quot; + jasonPrice[0] + &quot;원&quot;);
        System.out.println(&quot;홍차　:&quot; + jasonPrice[1] + &quot;원&quot;);
        System.out.println(&quot;커피 :&quot; + jasonPrice[2] + &quot;원&quot;);

        System.out.println(&quot;===== 강남점 =====&quot;);
        System.out.println(&quot;차　　:&quot; + gangnamPrice[0] + &quot;원&quot;);
        System.out.println(&quot;홍차　:&quot; + gangnamPrice[1] + &quot;원&quot;);
        System.out.println(&quot;커피 :&quot; + gangnamPrice[2] + &quot;원&quot;);
    }
}</code></pre><p>⭐포인트</p>
<p>배열 변수에 다른 배열 변수를 대입하면, 각각은 같은 실체를 참조 (공유) 한다.
배열의 요소 값을 복사하려면 대상 인덱스로 지정한다.</p>
<h3 id="배열의-길이-얻는-법">배열의 길이 얻는 법</h3>
<p>배열의 길이를 알고 싶은 경우는 배열의 length 라고 하는 속성을 사용해 취득할 수가 있다.</p>
<blockquote>
</blockquote>
<p>배열명.length
jasonAPT.length</p>
<p>ex&gt;</p>
<pre><code>public class ArrayLength {

    public static void main(String[] args) {
        // 배열변수선언과 요소 만들기
        int[] intArray = new int[3];

        System.out.println(&quot;intArray요소수 :、&quot; + intArray.length);
    }
}</code></pre><p>result&gt;</p>
<pre><code>int[] intArray = new int[3];</code></pre><h3 id="배열과-반복처리의-조합">배열과 반복처리의 조합</h3>
<p>배열을 for문과 조합하면 배열의 특성을 살린 효울적인 프로그램을 만들 수 있다.
배열의 인덱스에는 변수를 사용할 수 있으므로 반복문으로 취급하는 카운터 변수도 배열의 인덱스로서 이용할 수 있다.
아래 구문은 배열과 반복문을 결합하여 배열 변수 array의 모든 요소를 표시하는 프로그램의 일부이다. </p>
<pre><code>int[] array = {3, 5, 10};
for(int i=0; i &lt; array.length; i++) {
   System.out.println(&quot;array[&quot; + i + &quot;]로 &quot; + array[i]);
}</code></pre><p>카운터 변수를 0으로 초기화하고 &quot;i가 배열의 길이보다 작다&quot;라는 조건식을 설정하는 것으로
카운터 변수 i는 배열 변수 array의 모든 요소의 인덱스로 사용 가능하다.
첫번째 요소의 인덱스는 0이고 가장 마지막 요소의 인덱스는 배열의 길이 -1이기 때문이다.</p>
<h3 id="다차원-배열">다차원 배열</h3>
<h4 id="2차원-배열의-선언">2차원 배열의 선언</h4>
<blockquote>
<p>[예시]
int[][]  jasonAPT = new int[4][3];
자료형[][] 배열명 = new 자료형명[1차원배열요소수][2차원배열요소수];</p>
</blockquote>
<p>&lt;이중반복과 2차원 배열을 조합하여 만드는 구구단 프로그램&gt;</p>
<pre><code>public class TwoDimensionArrayWithKukuDan {

    public static void main(String[] args) {

        // 구구단 답을 관리하기 위한 2차원 배열 
        int[][] kukuDan = new int[9][9];

        for(int dan = 0; dan &lt; 9; dan++) {
            for(int i = 0; i &lt; 9; i++) {
                kukuDan[dan][i] = (dan + 1) * (i + 1);
            }
        }

        for(int dan = 0; dan &lt; 9; dan++) {
            System.out.print((dan + 1) + &quot;단 : &quot;);
            for(int i = 0; i &lt; 9; i++) {
                System.out.print(kukuDan[dan][i] + &quot;\t&quot;);
            }
            System.out.println();
        }
    }
}</code></pre><h4 id="다차원-배열의-요소-초기화">다차원 배열의 요소 초기화</h4>
<blockquote>
<p>자료형명[][] 배열명 = {
{값, 값, … },
{값, 값, … }
};
int[][] jasonAPT = {
{3, 1, 10},
{1, 3, 6 }
};</p>
</blockquote>
<pre><code>public class TwoDimensionArrayInitializer {

    public static void main(String[] args) {

        // int형 2차원배열의 선언과 요소의 초기화 
        int[][] intArray = {
                {50,150,250},
                {300,200,100}
        };

        for(int i = 0; i &lt; intArray.length; i++){
            for(int j = 0; j &lt; intArray[i].length; j++){
                System.out.println(&quot;intArray[&quot;+i+&quot;][&quot;+j+&quot;]값 : &quot; + intArray[i][j]);
            }
            System.out.println();
        }
    }
}</code></pre><p>⭐포인트
1)배열을 사용하면 같은 형태의 데이터를 정리해 관리할 수 있다.
2)순서별로 각각의 삽입물을 요소라고 부른다.
3)배열의 요소에 대한 번호를 인덱스라고합니다.
4)배열은 선언 후에 기억 영역의 수(길이)를 확보할 필요가 있다.
5)배열의 요소에 대입된 값을 취급하려면 , 인덱스를 지정한다.
6)배열 변수에 배열 변수를 대입하면, 같은 배열을 참조(공유)하게 된다.
7)반복문(특히 for문)과 배열을 조합하면 효율적인 처리가 가능하다.
8)인덱스에는 변수를 이용할 수도 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Java 05 반복처리]]></title>
            <link>https://velog.io/@hazel_1130/Java-05-%EB%B0%98%EB%B3%B5%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@hazel_1130/Java-05-%EB%B0%98%EB%B3%B5%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Mon, 25 Apr 2022 07:33:08 GMT</pubDate>
            <description><![CDATA[<h2 id="반복문">반복문</h2>
<p>같은 처리를 반복하는 것을 처리의 반복이라고 한다
Java에서 자주 이용되는 반복문은 for, while, do-while이 있다</p>
<h2 id="for문">for문</h2>
<p>for문은 반복의 횟수가 결정된 경우에 사용
&lt;기본 구문&gt;</p>
<pre><code>for(식1; 식2; 식3) {
    반복처리할 내용
}</code></pre><p>식1 : 카운터 변수 초기화
식2 : 반복 처리에 들어가는 조건
식3 : 반복처리시의 업데이트 처리식</p>
<h2 id="while문">while문</h2>
<p>조건식이 성립되는 동안에만 동일한 처리를 반복
&lt;기본구문&gt;</p>
<pre><code>while (조건식) {
    반복 실행내용
}</code></pre><h2 id="break문과-continue문">break문과 continue문</h2>
<p>break문은 &quot;반복처리를 빠지는&quot; 작업
주로 if문과 함께 사용
&lt;기본 사용법&gt;</p>
<pre><code>for(식1;식2;식3){
    if(조건식) {
        break;
      }
}
</code></pre><p>continue문은 후 처리를 건너 뛰고 원래 반복문의 시작 부분으로 돌아가 처리를 계속한다
주로 if문과 함께 사용된다
&lt;기본 구문&gt;</p>
<pre><code>for(조건식) {
    if (조건식) {
        continue;
        }
    }
}</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[Java 04 - 조건분기 ]]></title>
            <link>https://velog.io/@hazel_1130/Java-04-%EC%A1%B0%EA%B1%B4%EB%B6%84%EA%B8%B0</link>
            <guid>https://velog.io/@hazel_1130/Java-04-%EC%A1%B0%EA%B1%B4%EB%B6%84%EA%B8%B0</guid>
            <pubDate>Tue, 05 Apr 2022 10:57:39 GMT</pubDate>
            <description><![CDATA[<h2 id="조건분기란">조건분기란?</h2>
<p>&#39;만약 a라면 b한다&#39;라는 관계가 성립하는 처리를 프로그램으로 표현하는 것
이번 강의에서는 조건분기문으로 다음 세가지를 배웠다.</p>
<p>1)if문
2)if - else문
3)if- else i - else문</p>
<h2 id="if문">if문</h2>
<p>조건문의 가장 기본적인 구문
형식은 </p>
<pre><code>if (조건식) {
    처리내용 
}</code></pre><p>으로 구성되며 조건식의 조건을 만족하면 처리내용을 수행한다.    </p>
<p>조건식을 작성하기 위해서는 관계 (비교) 연산자 또한 숙지해야한다.
<img src="https://velog.velcdn.com/cloudflare/hazel_1130/e47897b8-c2c2-4abf-9ff7-eef069033de5/image.png" alt=""></p>
<p><a href="https://devbro.xyz/wp-content/uploads/2022/03/java-ch05-017.png">참조링크</a></p>
<p>if문의 조건식 안에 관계 연산자를 넣어 조건식이 거짓인지 참인지 판명할 때 쓰인다.
그러나 조건식 안에 문자열끼리의 비교를 하고 싶을 경우 == 를 쓰는 것이 아닌 equals()메서드를 사용하여 비교해야 한다는 점을 주의하자.</p>
<h2 id="if-else문">if else문</h2>
<p>if문의 기본 조건식이 &#39;만약 a라면 b한다&#39;였다면 if else문은 &#39;만약 a라면 b한다. 그러나 그렇지 않을 경우 c한다.&#39;라는 조건분기를 처리하는 문법이다. 
기본 형식은</p>
<pre><code>if( 조건식 ) {
    처리 A    
} else {
    처리 B
}</code></pre><p>조건식이 참일 경우 결과값으로는 A를 처리한 결과가 나오게 되며 그렇지 않을 경우 B가 나오게 된다.</p>
<h2 id="if-else-if--else문">if else-if -else문</h2>
<p>&#39;만약 a라면 b한다. 그러나 그렇지 않을 경우 c한다. 그 어느쪽도 아니라면 D 한다.&#39;
기본 형식은</p>
<pre><code>if( 조건식 1) {
    처리 A
}else if ( 조건식 2) {
    처리 B
} else {
    처리C</code></pre><h2 id="중첩-if문">중첩 if문</h2>
<p>중첩이란 처리 중 추가 제어문을 &#39;중첩&#39; 시키는 구조를 말한다.</p>
<p>중첩 if문의 기본 구문 (1)</p>
<pre><code>if (조건식 1) {
    실행문 A
    if (조건식 2) {
        실행문 B
   }
}</code></pre><p> 이 경우 조건식1이 성립할 경우 실행문 A가 실행된다.  조건식 1만 성립할 경우에는 실행문 A에서 코드가 끝난다. 그러나 그 다음 조건식 2가 평가되어 조건이 성립했을시에는 그 안의 실행문 B도 실행한다.</p>
<p>중첩 if문의 기본 구문 (2)</p>
<pre><code>if (조건식 1) {
    실행문 A
} else {
    실행문 B
    if (조건식 2) {
    실행문 C
    }
}</code></pre><p>이 경우 코드 실행은 </p>
<p>(조건식 1이 성립시) 조건식 1 평가 &gt; 실행문 A 실행 &gt; if문 마무리</p>
<p>(조건식 1이 성립하지 않을시) else 실행문 B 실행 &gt; 조건식 2 평가 &gt; (조건식 2 성립시) &gt; 실행문 C 실행 &gt; if문 마무리</p>
<h2 id="논리-연산자">논리 연산자</h2>
<p>논리 연산자는 복잡한 조건을 나타낼 때 주로 사용한다. 논리 연산자의 경우 좌우에 오는것은 반드시 true 혹은 false이거나 값으로 나타낼 수 있는 식이어야 한다.</p>
<p><img src="https://velog.velcdn.com/cloudflare/hazel_1130/84731c95-401e-4abc-be31-fb7c2c3c3555/image.png" alt="">
<a href="https://devbro.xyz/textbook/java-%ed%94%84%eb%a1%9c%ea%b7%b8%eb%9e%98%eb%b0%8d-%ea%b8%b0%ec%b4%88-%ea%b5%90%ec%9c%a1/6%ec%9e%a5-%ec%a1%b0%ea%b1%b4%eb%b6%84%ea%b8%b0/">참조 링크</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Java 03 - 프로그램 계산]]></title>
            <link>https://velog.io/@hazel_1130/03-Java-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EA%B3%84%EC%82%B0</link>
            <guid>https://velog.io/@hazel_1130/03-Java-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%A8-%EA%B3%84%EC%82%B0</guid>
            <pubDate>Tue, 05 Apr 2022 07:25:21 GMT</pubDate>
            <description><![CDATA[<h2 id="산술-연산자와-사칙연산">산술 연산자와 사칙연산</h2>
<p>산술 연산자란? 프로그램 연산에 사용하는 기호</p>
<p>예시) 아래의 예제 참고
package chap05;</p>
<p>public class ArithmeticOperator {
    public static void main(String[] args) {
        //계산용 값을 관리하는 변수를 선언하고 값을 할당
        int a = 9;
        int b = 4;</p>
<pre><code>    //응답변수
    int answer;

    System.out.print(&quot;a = &quot;);
    System.out.println(a);
    System.out.print(&quot;b = &quot;);
    System.out.println(b);

    //덧셈

    answer = a + b;
    System.out.print(&quot;a + b = &quot;);
    System.out.println(answer);

    //뺄셈
    answer = a - b;
    System.out.print(&quot;a - b = &quot;);
    System.out.println(a - b);

    //곱셈
    answer = a * b;
    System.out.print(&quot;a * b = &quot;);
    System.out.println(a * b);

    //나눗셈
    answer = a / b;

    double ans = Double.parseDouble(String.valueOf(a)) / Double.parseDouble(String.valueOf(b));
    System.out.print(&quot;a / b = &quot;);
    System.out.println(ans) ;

    //나머지
    answer = a % b;
    System.out.print(&quot;a % b = &quot;);
    System.out.println(a % b);
}</code></pre><p>}</p>
<p><a href="https://github.com/HelloHazel/javaBasic2022/blob/master/src/chap05/ArithmeticOperator.java">Git으로 코드 확인</a></p>
<p>위의 예제에서 확인 가능하듯 대부분의 산술연산자는 우리가 평소에 사용하는 사칙연산과 동일하다. %이 a를 b로 나눈 나머지라는 점만 유의할 것.</p>
<h2 id="산술연산자의-우선순위">산술연산자의 우선순위</h2>
<p>우선순위는 산수의 경우와 같다. 
예시)아래의 예제에서 확인
package chap05;</p>
<p>public class PriorityCheck {
    public static void main(String[] args) {</p>
<pre><code>    //계산용 값을 관리하는 변수를 선언하고 값할당
    int a = 9;
    int b = 1;
    int c = 3;

    int answer;
    System.out.print(&quot;a = &quot;);
    System.out.println(a);

    System.out.print(&quot;b = &quot;);
    System.out.println(b);

    System.out.print(&quot;c = &quot;);
    System.out.println(c);

    answer = a + b * c;
    System.out.print(&quot;a + b * c = &quot;);
    System.out.println(answer);

    answer = (a + b) * c;
    System.out.print(&quot;(a + b) * c = &quot;);
    System.out.println(answer);
}

}
&lt;실행결과&gt;
a = 9</code></pre><p>b = 1
c = 3
a + b * c = 12
(a + b) * c = 30</p>
<p>종료 코드 0(으)로 완료된 프로세스</p>
<p>위의 예제에서 알 수 있듯
우선순위는 괄호가 제일 높으며 그다음은 * / % 그다음은 + - 순이다.
때문에 코드 산술식에서 원하는 값을 얻지 못했을 경우 괄호 처리를 잘 해두었는지 확인할 것!</p>
<h2 id="증가-및-감소">증가 및 감소</h2>
<p>1) 증가 연산자 및 감소 연산자</p>
<p><img src="https://velog.velcdn.com/cloudflare/hazel_1130/bcacf155-9e5b-4acd-a5b6-e077bd56474c/image.png" alt="">
<a href="https://devbro.xyz/textbook/java-%ed%94%84%eb%a1%9c%ea%b7%b8%eb%9e%98%eb%b0%8d-%ea%b8%b0%ec%b4%88-%ea%b5%90%ec%9c%a1/5%ec%9e%a5-%ed%94%84%eb%a1%9c%ea%b7%b8%eb%9e%a8-%ea%b3%84%ec%82%b0/">참조링크</a></p>
<p>연산자를 앞에 사용하는 것을 &#39;전치&#39;라고 하며 뒤에 사용하는 것을 &#39;후치&#39;라고 한다.
증가 연산자와 감소 연산자는 연산자의 위치에 따라 처리 순서가 바뀌기 때문에 주의해야 한다.</p>
]]></description>
        </item>
    </channel>
</rss>