<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>nawhes_joo.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Tue, 09 Sep 2025 11:42:03 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>nawhes_joo.log</title>
            <url>https://velog.velcdn.com/images/nawhes_joo/profile/3bac5052-2fd6-43e5-9172-08e44b45f634/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. nawhes_joo.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/nawhes_joo" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[3. 회원 관리 예제 - 백엔드 개발]]></title>
            <link>https://velog.io/@nawhes_joo/3.-%ED%9A%8C%EC%9B%90-%EA%B4%80%EB%A6%AC-%EC%98%88%EC%A0%9C-%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C</link>
            <guid>https://velog.io/@nawhes_joo/3.-%ED%9A%8C%EC%9B%90-%EA%B4%80%EB%A6%AC-%EC%98%88%EC%A0%9C-%EB%B0%B1%EC%97%94%EB%93%9C-%EA%B0%9C%EB%B0%9C</guid>
            <pubDate>Tue, 09 Sep 2025 11:42:03 GMT</pubDate>
            <description><![CDATA[<h1 id="비즈니스-요구사항-정리">비즈니스 요구사항 정리</h1>
<ul>
<li><p>데이터 : 회원ID, 이름</p>
</li>
<li><p>기능 : 회원 등록, 조회</p>
</li>
<li><p>아직 데이터 저장소가 선정되지 않음(가상의 시나리오)
<img src="https://velog.velcdn.com/images/nawhes_joo/post/aeb1d9be-09f8-4e4b-a40e-995896a6796b/image.png" alt=""></p>
</li>
<li><p>컨트롤러 : 웹 MVC의 컨트롤러 역할</p>
</li>
<li><p>서비스 : 핵심 비즈니스 로직 구현</p>
</li>
<li><p>리포지토리 : 데이터베이스에 접근, 도메인 객체를 DB에 저장하고 관리</p>
</li>
<li><p>도메인 : 비즈니스 도메인 객체, 예) 회원, 주문, 쿠폰 등등 주로 데이터베이스에 저장하고 관리됨</p>
<p>클래스 의존관계
<img src="https://velog.velcdn.com/images/nawhes_joo/post/4fe87f9d-3e41-4e54-9e0d-cb8190f06415/image.png" alt=""></p>
</li>
<li><p>아직 데이터 저장소가 선정되지 않아서, 우선 인터페이스로 구현 클래스를 변경할 수 있도록 설계</p>
</li>
<li><p>데이터 저장소는 RDB, NoSQL 등등 다양한 저장소를 고민중인 상황으로 가정</p>
</li>
<li><p>개발을 진행하기 위해서 초기 개발 단계에서는 구현체로 가벼운 메모리 기반의 데이터 저장소 사용</p>
</li>
</ul>
<hr>
<h1 id="회원-도메인과-리포지토리-만들기">회원 도메인과 리포지토리 만들기</h1>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/d0d14dbd-7b79-4f80-a894-fc9370198bcf/image.png" alt=""></p>
<pre><code class="language-java">package study.hello_spring.domain;

public class Member {

    private Long id;
    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}</code></pre>
<p>domain이라는 Package를 만들고, Member라는 Class를 생성한다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/563c6571-7c1d-4008-b48d-14b4ab94f6df/image.png" alt=""></p>
<pre><code class="language-java">package study.hello_spring.Repository;

import study.hello_spring.domain.Member;

import java.util.List;
import java.util.Optional;

public interface MemberRepository {
    Member save(Member member); // 저장
    Optional&lt;Member&gt; findById(Long id); // id로 찾기
    Optional&lt;Member&gt; findByName(String name); // 이름으로 찾기
    List&lt;Member&gt; findAll(); // 저장된 모든 회원 리스트를 반환
}
</code></pre>
<p>Repository Package를 생성하고 MemberRepository Interface를 생성한다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/aca395f9-2dd6-455b-a10a-4f04117555e6/image.png" alt=""></p>
<pre><code class="language-java">package study.hello_spring.Repository;

import study.hello_spring.domain.Member;

import java.util.*;

public class MemoryMemberRepository implements MemberRepository{

    private static Map&lt;Long, Member&gt; store = new HashMap&lt;&gt;();
    private static long sequence = 0L;
    // 동시성 문제가 고려되어 있지 않음, 실무에서는 ConcurrentHashMap, AtomicLong 사용 고려

    @Override
    public Member save(Member member) {
        member.setId(++sequence); // sequence 값 증가
        store.put(member.getId(), member);
        return member;
    }

    @Override
    public Optional&lt;Member&gt; findById(Long id) {
        return Optional.ofNullable(store.get(id)); // null 가능
    }

    @Override
    public Optional&lt;Member&gt; findByName(String name) {
        return store.values().stream() //컬렉션(List, Set 등)에서 호출하는 메서드
                .filter(member -&gt; member.getName().equals(name)) // 스트림 안의 요소들을 조건에 맞는 것만 걸러내기
                .findAny(); // 스트림에서 조건에 맞는 요소 중 아무거나 하나를 꺼냄
    }

    @Override
    public List&lt;Member&gt; findAll() {
        return new ArrayList&lt;&gt;(store.values()); // ArrayList : 자바스크립트 배열처럼 가변적인 리스트
    }
}</code></pre>
<p>설명은 코드에 주석으로 작성</p>
<p>MemoryMemberRepository 클래스를 생성하여 리포지토리를 만들었다.</p>
<p>이제 테스트를 할 차례이다.</p>
<hr>
<h1 id="회원-리포지토리-테스트-케이스-작성">회원 리포지토리 테스트 케이스 작성</h1>
<blockquote>
<p>개발한 기능을 실행해서 테스트 할 때 자바의 main 메서드를 통해서 실행하거나, 웹 애플리케이션의 컨트롤러를 통해
서 해당 기능을 실행한다. 이러한 방법은 준비하고 실행하는데 오래 걸리고, 반복 실행하기 어렵고 여러 테스트를 한번
에 실행하기 어렵다는 단점이 있다. 자바는 JUnit이라는 프레임워크로 테스트를 실행해서 이러한 문제를 해결한다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/29146922-4601-4e9c-9c88-604055bf7c15/image.png" alt=""></p>
<p>test에 main과 같이 repository 패키지를 생성하고 MemoryMemberRepositoryTest 클래스를 생성한다.</p>
<pre><code class="language-java">package study.hello_spring.repository;

import org.junit.jupiter.api.Test;

class MemoryMemberRepositoryTest {

    MemberRepository repository = new MemoryMemberRepository();

    @Test
    public void save(){

    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/7ad81ef4-b350-48e2-9186-69e2c8cf4f63/image.png" alt=""></p>
<p>그 후에 좌측 실행 버튼을 클릭하면</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/85cc2b39-ce06-4476-9f87-7d830c30367c/image.png" alt=""></p>
<p>위 사진과 같이 Run 탭이 실행된다면 성공이다.</p>
<hr>
<h2 id="save">save</h2>
<pre><code class="language-java">@Test
public void save(){
    Member member = new Member();   // member 생성
    member.setName(&quot;spring&quot;);   // &#39;spring&#39; 이라는 이름을 설정 

    repository.save(member);    // repository에 member 저장

    Member result = repository.findById(member.getId()).get();  // member.getId()로 Id를 불러오고, findbyId로 repository에서 member를 찾은 후 get()으로 가져옴
    System.out.println(&quot;reuslt = &quot; + (result == member));
}</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/3e7130e1-bb8d-41ed-aff3-c2398d13bf5a/image.png" alt=""></p>
<p>true가 출력된다.</p>
<pre><code class="language-java">Assertions.assertEquals(member, result);    // 기대값과 실제값을 비교</code></pre>
<p>System.out.print 대신, 값을 비교하는 매서드인 Assertion.assertEquals를 사용해보자.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/2a944025-48c5-4bd5-a979-aa2bc6ab2bd3/image.png" alt=""></p>
<p>완료 마크가 보인다.</p>
<p>member와 result가 같다는 뜻이다.</p>
<p>그럼 result 대신 null을 넣으면 어떻게 될까?</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/fe223c44-d1a3-41d8-b9a9-9aaefd281815/image.png" alt=""></p>
<p>위 사진과 같이 오류가 발생한다.</p>
<blockquote>
<p>Assertions.assertThat(member).isEqualTo(result);</p>
</blockquote>
<p>또한 비교하는 매서드이다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/1ef30559-781c-40e2-a036-9c40486d9d1c/image.png" alt=""></p>
<p>Assertions에 커서를 두고 option + enter를 누르면 static import를 할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/e37a8889-e023-48eb-90dc-8aedff71f5b0/image.png" alt=""></p>
<p>import static이 추가되고, Assertions. 없이 assertThat을 바로 사용할 수 있다.</p>
<hr>
<h2 id="findbyname">findByName</h2>
<pre><code class="language-java">@Test
public void findByName(){
    Member member1 = new Member();  // member1 생성
    member1.setName(&quot;spring1&quot;);     // &#39;spring1&#39; 지명
    repository.save(member1);       // member1에 저장

    Member member2 = new Member();
    member2.setName(&quot;spring2&quot;);
    repository.save(member2);

    Member result = repository.findByName(&quot;spring1&quot;).get(); // result에 &quot;spring1&quot; 으로 findByName

    assertThat(result).isEqualTo(member1);
}</code></pre>
<p>이번엔 findByName 테스트해보자.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/667a1a03-55b3-47ba-891a-c56a24363db6/image.png" alt=""></p>
<p>member1에 setName(&quot;spring1&quot;)을 했으니 성공한 모습.</p>
<p><code>repository.findByName(&quot;spring2&quot;).get();</code> 으로 수정하고 실행하면</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/b5ddcb93-4bbd-4702-9a70-df7ff44723a0/image.png" alt=""></p>
<p>예상대로 오류가 발생한다.</p>
<hr>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/76aaa1f7-96d3-45f3-bf6a-314ac3c1d2ed/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/543f5508-bce5-4c55-afff-1c945c1ce286/image.png" alt=""></p>
<p>테스트를 동시에 실행하려면 위 사진과 같이 class에서 실행하면 해당 클래스에 있는 모든 테스트를 실행할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/ce863286-0bba-46af-8c2e-4c611c69ff36/image.png" alt=""></p>
<ul>
<li>단축키</li>
</ul>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/66978a9f-b83b-49f9-a194-cb1ef90869f9/image.png" alt=""></p>
<p>shift + F6을 누르면 위 사진과 같이 rename이 가능하다.</p>
<hr>
<h2 id="findall">findAll</h2>
<pre><code class="language-java">@Test
public void findAll(){
    Member member1 = new Member();
    member1.setName(&quot;spring1&quot;);
    repository.save(member1);

    Member member2 = new Member();
    member2.setName(&quot;spring2&quot;);
    repository.save(member2);

    List&lt;Member&gt; result = repository.findAll();

    assertThat(result.size()).isEqualTo(2);
}</code></pre>
<p>이번에도 member1, member2를 만든 후 테스트해보자.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/e0b03fe1-faa7-416f-a217-7bb6449e9cad/image.png" alt=""></p>
<p>result.size가 2이므로 정상적으로 작동한다.</p>
<p>isEqualTo(3)으로 수정하면</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/6a092d44-3d8c-40a7-aa24-790199aef88c/image.png" alt=""></p>
<p>예상대로 오류가 발생한다.</p>
<hr>
<h2 id="aftereach">AfterEach</h2>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/4b157569-d4d9-4076-8292-f64a02b41795/image.png" alt=""></p>
<p>정상적으로 작동하도록 코드를 수정하고 class단에서 테스트를 모두 실행했더니 위 사진처럼 오류가 발생한다.</p>
<p>같은 저장소를 사용하고 있어 테스트 간 간섭이 발생하여 오류가 발생하는 것이다.</p>
<pre><code class="language-java">class MemoryMemberRepositoryTest {

    MemoryMemberRepository repository = new MemoryMemberRepository();

    @AfterEach
    public void afterEach(){
        repository.clearStore(); // 저장소 클리어
    }

    ...
</code></pre>
<p>MemoryRepository를 MemoryMemberRepository로 변경 후</p>
<p>MemoryMemberRepository.java 파일에</p>
<pre><code class="language-java">public void clearStore(){
        store.clear();
    }</code></pre>
<p>위 코드를 추가해준다.</p>
<p>@AfterEach는 각 테스트가 끝난 후 실행된다는 뜻이다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/373f6f06-8004-48c9-816d-09d89336522a/image.png" alt=""></p>
<p>각 테스트 후 clearStore()를 실행해주면서 테스트 간 간섭이 일어나지 않는다.</p>
<ul>
<li>같은 저장소를 사용하고 있어 테스트 간 간섭이 발생한다.</li>
<li>테스트 순서는 보장할 수 없다.</li>
<li>테스트 하나가 끝나고 나면 데이터를 클리어해줘야 한다.</li>
</ul>
<hr>
<h1 id="회원-사이트-개발">회원 사이트 개발</h1>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/207e7439-e82c-443e-89d8-9d9d234021e4/image.png" alt=""></p>
<p>repository 아래에 service 패키지를 만들고 MemberService 클래스를 생성한다.</p>
<pre><code class="language-java">package study.hello_spring.repository.service;

import study.hello_spring.domain.Member;
import study.hello_spring.repository.MemberRepository;
import study.hello_spring.repository.MemoryMemberRepository;

import java.util.Optional;

public class MemberService {

    private final MemberRepository memberRepository = new MemoryMemberRepository();

    /*
     * 회원 가입
     */
    public Long join(Member member) {
        // 같은 이름이 있는 중복 회원 x
        Optional&lt;Member&gt; result = memberRepository.findByName(member.getName());    // Optional : 값이 있을 수도 있고 없을 수도 있는 상황을 표현하는 &quot;상자&quot; 같은 객체
        result.ifPresent(m -&gt; { // ifPresent : 값이 있으면
            throw new IllegalStateException(&quot;이미 존재하는 회원입니다.&quot;);  // throw : 예외(에러)를 던짐, IllegalStateException : 자바 런타임 예외 클래스
        });
        // result.orElseGet 도 있음
        memberRepository.save(member);
        return member.getId();
    }
}</code></pre>
<p>이름이 중복되지 않게 설계했으니
이미 중복된 이름이면 throw, 중복되지 않은 이름이면 save 후 getId()</p>
<pre><code class="language-java">Optional&lt;Member&gt; result = memberRepository.findByName(member.getName()); 
        result.ifPresent(m -&gt; { 
            throw new IllegalStateException(&quot;이미 존재하는 회원입니다.&quot;);  
        });</code></pre>
<p>부분을</p>
<pre><code class="language-java">memberRepository.findByName(member.getName())
                .ifPresent(m -&gt; {
                throw new IllegalStateException(&quot;이미 존재하는 회원입니다.&quot;);
            });</code></pre>
<p>이렇게 표현할 수 있다.</p>
<p>위 코드를 선택하고 <em>control + t</em> 를 누르면</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/0ba3762b-338a-486b-a352-8cf37c166430/image.png" alt=""></p>
<p>위 사진과 같이 refactor 할 수 있다.</p>
<p>Extract Method를 사용하면</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/b81f024d-da04-4394-b3f8-32a15c541692/image.png" alt=""></p>
<p>위 사진과 같이 메서드가 생성된다.</p>
<hr>
<pre><code class="language-java">/*
 * 전체 회원 조회
 */
public List&lt;Member&gt; findMembers(){
    return memberRepository.findAll();
}

public Optional&lt;Member&gt; findOne(Long memberId){
    return memberRepository.findById(memberId);
}</code></pre>
<p>회원 조회하는 메서드도 작성하고 테스트해보자.</p>
<hr>
<h1 id="회원-서비스-테스트">회원 서비스 테스트</h1>
<p>지난 테스트에서는 test에 패키지를 만들고 직접 작성했는데 간단한 방법이 있다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/c4a990ef-0cf1-4b28-b7f1-c0ff6e58ed2c/image.png" alt=""></p>
<p>클래스 내부에 커서를 잡고 <em>command + shift + t</em> 단축키를 입력하면 위 사진처럼 Create 할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/38dae7b1-1373-4629-8319-b1f6dd444fc0/image.png" alt=""></p>
<ul>
<li>Testing library는 JUnit5로 두고</li>
<li>Class name을 입력한 후</li>
<li>Test 하고 싶은 메서드를 선택</li>
<li>OK 클릭</li>
</ul>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/7db62c4e-91a7-4bb6-84f9-231f213ccbfe/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/1eb9fbaf-831f-451c-9342-0e82e1d5e4da/image.png" alt=""></p>
<p>간단하게 Test용 껍데기 파일이 생성된다.</p>
<ul>
<li><img src="https://velog.velcdn.com/images/nawhes_joo/post/8e55f1db-ea98-4076-8a6f-4b29e674e29c/image.png" alt=""></li>
</ul>
<p>Build 될 때 Test 코드는 실제 코드에 포함되지 않는다.</p>
<p>그래서 직관적으로 알아볼 수 있도록 과감하게 한글로 바꿔도 무방하다.</p>
<hr>
<h2 id="회원가입">회원가입</h2>
<pre><code class="language-java">MemberService memberService = new MemberService();</code></pre>
<p>memberService를 새로 생성하고</p>
<pre><code class="language-java">@Test
void 회원가입() {
    // given    상황
    Member member = new Member();
    member.setName(&quot;hello&quot;);

    // when        실행했을 때
    Long saveId = memberService.join(member);

    // then        결과
    Member findMember = memberService.findOne(saveId).get();
    assertThat(member.getName()).isEqualTo(findMember.getName());
}</code></pre>
<p>given, when, then 주법으로 테스트하는 걸 추천한다</p>
<p>member를 생성하여 setName(&quot;hello&quot;)
memberService에 방금 생성한 member를 join
결과 확인</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/a039f8f8-6843-425f-af3d-0788805b7458/image.png" alt=""></p>
<p>정상적으로 테스트 성공</p>
<p>하지만 테스트는 성공보단 예외(실패)가 더 중요하다.</p>
<p>중복 가입을 테스트해보자.</p>
<hr>
<h2 id="중복가입">중복가입</h2>
<pre><code class="language-java">@Test
public void 중복_회원_예외(){
    // given
    Member member1 = new Member();
    member1.setName(&quot;spring&quot;);

    Member member2 = new Member();
    member2.setName(&quot;spring&quot;);

    // when
    memberService.join(member1);
    try {    // 시도
        memberService.join(member2);
        fail();    // 실패
    } catch (IllegalStateException e){ // 예외처리
        assertThat(e.getMessage()).isEqualTo(&quot;이미 존재하는 회원입니다. 123&quot;); // 실패 메세지와 작성한 메세지가 같은지 확인
    }

    // then
}</code></pre>
<p>member1, member2에 둘 다 &quot;spring&quot; 이라고 setName을 한 후
memberService에 join을 해보았다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/705c9504-d27d-47e2-9cad-a9eee3b8c36a/image.png" alt=""></p>
<p>실패 메세지는 &quot;이미 존재하는 회원입니다.&quot; 이지만 뒤에 123을 붙여서 두 메세지가 같지 않으니 위 사진처럼 오류가 발생한다.</p>
<p>근데 이렇게 try catch를 사용하려니 뭔가 애매하다.</p>
<p>assertThrows를 사용해보자.</p>
<pre><code class="language-java">// when
memberService.join(member1);
assertThrows(IllegalStateException.class, () -&gt; memberService.join(member2));</code></pre>
<blockquote>
<p>assertThrows(예외클래스, 실행할코드);</p>
</blockquote>
<ul>
<li>예외클래스 → 어떤 예외가 발생해야 하는지 지정</li>
<li>실행할코드 → 예외가 발생할 것으로 기대하는 코드(람다로 작성)</li>
</ul>
<p>assertThrows는 &quot;이 코드 실행했을 때 정말로 IllegalStateException이 던져지는지 확인&quot;하는 역할을 한다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/4dee77a5-ca53-42d2-9639-726bf9c72ac0/image.png" alt=""></p>
<blockquote>
<ul>
<li>memberService.join(member2) 실행 도중 IllegalStateException이 발생하면
→ 예상한 대로 동작했으므로 테스트 성공</li>
</ul>
</blockquote>
<ul>
<li>만약 예외가 발생하지 않거나, 다른 종류의 예외가 발생하면
→ 예상과 다르므로 테스트 실패</li>
</ul>
<p>IllegalStateException이 발생하므로 테스트 결과는 통과인 것이다.</p>
<hr>
<p>만약 회원 가입의 setName에 &quot;hello&quot; 가 아닌 &quot;spring&quot;을 넣고 전체 실행하면 오류가 발생한다.</p>
<p>왜? 테스트하면서 &quot;spring&quot;이라는 member는 이미 memberRepository에 저장되어있기 때문이다.</p>
<p>그럴 땐 아까 배운</p>
<pre><code class="language-java">MemoryMemberRepository memberRepository = new MemoryMemberRepository();

@AfterEach
public void afterEach(){
    memberRepository.clearStore();
}</code></pre>
<p>를 추가해주면 clear되기 때문에 정상적으로 작동한다.</p>
<hr>
<p>테스트 할 때 </p>
<pre><code class="language-java">MemoryMemberRepository memberRepository = new MemoryMemberRepository();</code></pre>
<p>를 작성해서 하였다.</p>
<p>하지만 new로 새로 생성하기 때문에 memberService에서 사용하는 repository와 테스트 케이스에서 사용하는 repository는 서로 다르다.</p>
<p>이런 경우 테스트하면서 문제가 발생할 수 있다.</p>
<p>그럴 땐</p>
<pre><code class="language-java">public class MemberService {

    private final MemberRepository memberRepository;

    public MemberService(MemberRepository memberRepository){
        this.memberRepository = memberRepository;
    }
    ...</code></pre>
<p>외부에서 넣어주도록 바꿔준다.</p>
<p>테스트 케이스에서도</p>
<pre><code class="language-java">class MemberServiceTest {

    MemberService memberService;
    MemoryMemberRepository memberRepository;

    @BeforeEach    // 각 테스트 실행 전
    public void beforeEach(){
        memberRepository = new MemoryMemberRepository();
        memberService = new MemberService(memberRepository);
    }</code></pre>
<p>위처럼 수정한다.    </p>
<p>new MemberRepository로 생성 후 memberRepository에 넣고,
MemberService에 넣어준다.</p>
<p>이러면 같은 repository를 사용하게 된다.</p>
<p>위와 같은 구조를 DI는 Dependency Injection (의존성 주입) 라고 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Flutter - 커스텀 위젯]]></title>
            <link>https://velog.io/@nawhes_joo/Flutter-%EC%BB%A4%EC%8A%A4%ED%85%80-%EC%9C%84%EC%A0%AF</link>
            <guid>https://velog.io/@nawhes_joo/Flutter-%EC%BB%A4%EC%8A%A4%ED%85%80-%EC%9C%84%EC%A0%AF</guid>
            <pubDate>Thu, 22 Aug 2024 12:20:36 GMT</pubDate>
            <description><![CDATA[<h1 id="커스텀-위젯">커스텀 위젯</h1>
<pre><code class="language-dart">  Widget build(BuildContext context) {

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(),
        body: SizedBox(
          child: Text(&#39;안녕&#39;),
        ),
      ),
    );
  }</code></pre>
<p>이렇게 작성하면</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/7037a616-e080-469d-8e35-4b1d3d32ebd9/image.png" alt=""></p>
<p>body에 안녕이라는 텍스트가 출력된다.</p>
<p>만약, 레이아웃용 위젯들이 너무 길다면 매우 복잡하고 가독성도 많이 떨어질 것이다.</p>
<p>이 때 <code>커스텀 위젯</code>을 만들어서 깔끔하게 축약할 수 있다.</p>
<hr>
<h2 id="class">class</h2>
<p>빈 공간에 stless를 입력하여 StatelessWidget을 생성하고</p>
<pre><code class="language-dart">class CustomWidget extends StatelessWidget {
  const CustomWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return const Placeholder(); // 축약할 레이아웃 작성
  }
}</code></pre>
<p>위 코드와 같이 class를 작명한다.</p>
<p>그리고 return 뒤에 축약할 레이아웃을 넣으면 된다.</p>
<pre><code class="language-dart">class CustomWidget extends StatelessWidget {
  const CustomWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      child: Text(&#39;안녕&#39;),
    );
  }
}</code></pre>
<p>처음에 작성한 &#39;안녕&#39; 텍스트가 들어간 SizedBox 레이아웃을 넣었다.</p>
<p>이렇게 CustomWidget이라는 이름의 커스텀 위젯을 만든 것이다.</p>
<p>이제 <code>CustomWidget()</code>만 작성해도 CustomWidget에 작성한 레이아웃을 불러올 수 있다.</p>
<pre><code class="language-dart">class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(),
        body: CustomWidget(),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      child: Text(&#39;안녕&#39;),
    );
  }
}</code></pre>
<p>전체 코드의 모습이다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/270ac3f9-28dd-42ef-ad36-a44d6253a7ed/image.png" alt=""></p>
<p>커스텀 위젯으로 생성하기 전과 같은 결과를 확인할 수 있다.</p>
<p>위 예시의 레이아웃 길이가 짧기 때문에 잘 와닿지 않겠지만, 레이아웃이 길어지면 매우 유용하게 사용될 것이다.</p>
<p>물론 다른 dart 파일에 미리 커스텀 위젯을 작성한 후, import를 통해 작성한 커스텀 위젯을 불러올 수 있다.</p>
<hr>
<h2 id="변수">변수</h2>
<pre><code class="language-dart">var a = SizedBox(
    child: Text(&#39;안녕&#39;)
);

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

  @override
  Widget build(BuildContext context) {

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(),
        body: a,
      ),
    );
  }
}</code></pre>
<p>이런 식으로 변수나 함수에 담아 축약해도 된다. 하지만 성능상 이슈가 발생할 수도 있다. </p>
<p>그렇다면 변수에는 어떤 것들을 담을 수 있을까?</p>
<p>그것은 바로 <code>변하지 않는 것들</code>이다.</p>
<p>예를 들면 로고, 상단바, 하단바 등 변하지 않는 UI들은 변수 함수로 축약해도 상관이 없다.</p>
<hr>
<h2 id="주의사항">주의사항</h2>
<p>커스텀 위젯이 편하다고 아무거나 다 커스텀 위젯으로 만드는 것은 좋은 방법이 아니다.</p>
<p>대표적으로 state 관리가 힘들어진다는 단점이 있다.</p>
<p>그러므로 재사용이 많은 UI들, 또는 큰 UI(페이지들)을 커스텀 위젯으로 만들어서 사용하면 좋다.</p>
<hr>
<h1 id="listview">ListView</h1>
<pre><code class="language-dart">return MaterialApp(
      home: Scaffold(
        appBar: AppBar(),
        body: Column(
          children: [
            Text(&#39;안녕&#39;),
            Text(&#39;안녕&#39;),
            Text(&#39;안녕&#39;),
            Text(&#39;안녕&#39;),
            Text(&#39;안녕&#39;),
          ],
        ),
      ),
    );</code></pre>
<p>세로로 배치할 때 사용하는 Column이다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/f2e43b45-d583-48f8-af32-a52e05679fff/image.png" alt=""></p>
<p>하지만, 글자가 아무리 많더라도 스크롤바가 자동으로 생기지 않는다.</p>
<p>이럴 때 사용하는 것이 ListView이다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/8659c16e-8548-434d-8d3f-f48b070fa380/image.png" alt=""></p>
<p>Column 대신 ListView를 사용하면 위 사진과 같이 스크롤바가 생기는 것을 확인할 수 있다.</p>
<p>스크롤바 뿐만 아니라<code>controller</code>를 통해 스크롤 위치 감시도 가능하고, 메모리 절약도 가능하다.</p>
<blockquote>
<p>1~100까지의 글자가 있다면, 유저가 가장 아래쪽을 보고 있을 때, 스크롤해서 지나간 위쪽 부분은 메모리에서 삭제가 가능하다. 이런 이유로 메모리 절약(성능개선)이 가능하다.</p>
</blockquote>
<p>쇼핑몰, 인스타 피드 등 전부 ListView라고 보면 된다.</p>
<p>용도에 맞게 알맞은 위젯을 사용하여 개발해보자.</p>
<p>출처 - <a href="https://www.youtube.com/@codingapple">코딩애플</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Flutter - Flexible과 Expanded]]></title>
            <link>https://velog.io/@nawhes_joo/Flutter-Flexible-Expanded</link>
            <guid>https://velog.io/@nawhes_joo/Flutter-Flexible-Expanded</guid>
            <pubDate>Thu, 01 Aug 2024 02:30:27 GMT</pubDate>
            <description><![CDATA[<h1 id="flexible">Flexible</h1>
<pre><code class="language-dart">return MaterialApp(
      home: Scaffold(
        appBar: AppBar(),
        body: Row(
          children: [
            Container( width: 100, ),
            Container( width: 100, ),
          ],
        )

      )
    );</code></pre>
<p>이런 식으로 Container의 폭을 width로 줄 수 있다고 배웠다.</p>
<p>하지만 %로 주고 싶다면?</p>
<pre><code class="language-dart">Flexible(child: Container()),</code></pre>
<p>Container를 Flexible이라는 위젯으로 감싸야 한다.</p>
<pre><code class="language-dart">children: [
            Flexible(child: Container(color: Colors.blue,), flex: 3),
            Flexible(child: Container(color: Colors.green,), flex: 7),
          ],</code></pre>
<p>위 코드처럼 Flexible로 감싼 후 flex라는 파라미터를 이용하여 %로 width를 줄 수 있다.</p>
<p>flex : 3, flex : 7은 배율을 의미한다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/3e73ba1e-0e44-4b14-be5d-5ce071e0acee/image.png" alt=""></p>
<p>3대7 비율로 정상적으로 Container가 생성된 모습을 확인할 수 있다.</p>
<pre><code class="language-dart">body: Row(
          children: [
            Flexible(child: Container(color: Colors.blue,), flex: 5),
            Flexible(child: Container(color: Colors.green,), flex: 5),
          ],
        )</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/c9205d55-4817-4b2f-be11-82c324492032/image.png" alt=""></p>
<p>5대5</p>
<pre><code class="language-dart">body: Row(
          children: [
            Flexible(child: Container(color: Colors.blue,), flex: 5),
            Flexible(child: Container(color: Colors.green,), flex: 5),
            Flexible(child: Container(color: Colors.red,), flex: 5),
          ],
        )</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/12a310fd-c6a4-4243-b857-bcbb3e4d2a4f/image.png" alt=""></p>
<p>3등분</p>
<pre><code class="language-dart">body: Column(
          children: [
            Flexible(child: Container(color: Colors.blue,), flex: 5),
            Flexible(child: Container(color: Colors.green,), flex: 5),
            Flexible(child: Container(color: Colors.red,), flex: 5),
          ],
        )</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/be1b27b6-93e1-4207-a19a-cbc51574d517/image.png" alt=""></p>
<p>Row 뿐만 아니라 Column도 물론 가능하다.</p>
<hr>
<h1 id="expanded">Expanded</h1>
<pre><code class="language-dart">body: Row(
          children: [
            Expanded(child: Container(color: Colors.blue,)),
            Container(width: 100, color: Colors.green,),
          ],
        )</code></pre>
<p>Row() 안에서 박스 하나만 꽉 채우고 싶으면 Expanded()를 사용하면 된다.</p>
<p>flex : 1 가진 Flexible 박스와 똑같다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/6946688a-a3a6-43aa-a2b4-5b14cdc925e4/image.png" alt=""></p>
<p>위 사진처럼 초록색의 width 100 Container를 제외한 부분은 Expanded를 사용한 파란색 Container가 채워진다.</p>
<hr>
<h1 id="devtool">DevTool</h1>
<p>박스를 디자인했는데 의도와 다르다면 의심해볼 수 있는 대표적인 경우는 사이즈가 이상하거나 박스 위치가 이상한 경우이다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/faf68ed2-6080-4ec6-a289-fee5d18a0a24/image.png" alt=""></p>
<p>그럴 땐 콘솔창에 있는 DevTools를 활용해보자.</p>
<p>콘솔창이 안보이면 alt + 4를 누르면 콘솔창이 열릴 것이다.</p>
<p><code>alt + f4 아님!!</code></p>
<p>Open Flutter DevTools를 클릭하면</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/09e5f777-e06b-4d76-8fef-1f6ed972441f/image.png" alt=""></p>
<p>크롬창으로 DevTools가 뜬다.</p>
<p>좌측 Widget Tree에서 두 번째 Container의 width가 100이라는 것을 확인할 수 있다.</p>
<p>이 방법으로 디버깅해보자.</p>
<p>출처 <a href="https://www.youtube.com/@codingapple">코딩애플</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Flutter - Appbar]]></title>
            <link>https://velog.io/@nawhes_joo/Flutter-Appbar</link>
            <guid>https://velog.io/@nawhes_joo/Flutter-Appbar</guid>
            <pubDate>Thu, 25 Jul 2024 12:53:54 GMT</pubDate>
            <description><![CDATA[<h1 id="글자와-아이콘-스타일">글자와 아이콘 스타일</h1>
<pre><code class="language-dart">class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar( title: Text(&#39;앱임&#39;)),
        body: SizedBox(
          child: Text(&#39;안녕하세요&#39;)
        ),
      )
    );
  }
}</code></pre>
<p>위와 같이 작성하면 </p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/5752194f-43d1-478f-b0fd-c2635e6ea95d/image.png" alt=""></p>
<p>이런 텍스트가 출력된다.</p>
<p>텍스트에 스타일을 추가해보자.</p>
<h2 id="color">Color</h2>
<h3 id="colorsred">Colors.red</h3>
<pre><code class="language-dart">class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar( title: Text(&#39;앱임&#39;)),
        body: SizedBox(
          child: Text(&#39;안녕하세요&#39;,
            style: TextStyle( color: Colors.red ),
          )
        ),
      )
    );
  }
}</code></pre>
<p>Text 안에 style: TextStyle을 추가하였다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/e7380b26-f06c-4cdf-99df-4c5ed99a710c/image.png" alt=""></p>
<p>예상대로 빨간색의 글자가 출력된다.</p>
<hr>
<h3 id="color-1">Color()</h3>
<p>색상 코드로 스타일을 주고 싶을 땐 Color()을 이용하자.</p>
<pre><code class="language-dart">class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar( title: Text(&#39;앱임&#39;)),
        body: SizedBox(
          child: Text(&#39;안녕하세요&#39;,
            style: TextStyle( color: Color(0xffa30000) ),
          )
        ),
      )
    );
  }
}</code></pre>
<p>Colors.red가 아닌 Color(0xffa30000)을 입력하였다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/7db3b02a-36e8-4bfb-b9be-50c321bc769a/image.png" alt=""></p>
<p>여기서 주의할 점은, 색상코드 앞에 0xff를 꼭 붙여야 한다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/711b164a-fea9-4bc6-9ec0-5e5d9d0fa02c/image.png" alt=""></p>
<p>Color(0xffa30000)을 입력한 라인 왼쪽에 현재 코드의 색이 보인다.</p>
<p>저 색 상자를 클릭하면 사진처럼 색깔을 정할 수 있다.</p>
<hr>
<h3 id="colorfromrgbo">Color.fromRGBO</h3>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/eb4d1ea7-1887-4943-be7b-fdb9882028ad/image.png" alt=""></p>
<p>rgbo를 입력하여 색을 추가할 수도 있다.</p>
<hr>
<h2 id="fontweight">fontWeight</h2>
<pre><code class="language-dart">style: TextStyle( fontWeight: FontWeight.w700),</code></pre>
<p>fontWeight: FontWeight.~~ 을 통해 굵기도 설정할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/e49fc061-0bde-43d9-95bb-2f732d463946/image.png" alt=""></p>
<p>w700을 적용한 모습</p>
<p>Icon 또한 size: <del>, color: ~</del> 을 통해 스타일을 줄 수 있다.</p>
<hr>
<h1 id="버튼-넣는-법과-스타일">버튼 넣는 법과 스타일</h1>
<h2 id="textbutton">TextButton</h2>
<pre><code class="language-dart">child: TextButton( child: , onPressed: (){}, )</code></pre>
<p>버튼은 TextButton()을 이용해서 넣을 수 있다.</p>
<p>이 때, onPressed(){}을 입력하지 않으면 오류가 발생한다고 한다.</p>
<pre><code class="language-dart">    return MaterialApp(
      home: Scaffold(
        appBar: AppBar( title: Text(&#39;앱임&#39;)),
        body: SizedBox(
          child: TextButton(
            child: Text(&#39;글자&#39;),
            onPressed: (){},
          )
        ),
      )
    );</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/932d5060-22fb-439d-bf8c-99b45bc59f1d/image.png" alt=""></p>
<p>위 사진처럼 텍스트로 된 버튼을 넣을 수 있다.</p>
<p>하지만 너무 못생겼다.</p>
<h2 id="elevatedbutton">ElevatedButton</h2>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/58eda3bc-15ff-4b2a-83e1-580db7313e03/image.png" alt=""></p>
<p>TextButton 대신 ElevatedButton을 입력하였다.</p>
<p>TextButton 보단 나은 거 같다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/09f66228-f647-436e-aa50-6143e717affc/image.png" alt=""></p>
<p>위 사진처럼 Style을 추가할 수 있다.</p>
<hr>
<h2 id="iconbutton">IconButton</h2>
<pre><code class="language-dart">    return MaterialApp(
      home: Scaffold(
        appBar: AppBar( title: Text(&#39;앱임&#39;)),
        body: SizedBox(
          child: IconButton(
            icon: Icon(Icons.star),
            onPressed: (){},
            style: ButtonStyle(),
          )
        ),
      )
    );</code></pre>
<p>TextButton이 있다면 IconButton도 있다. 위와 같이 작성하면</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/a673a5cf-abd8-4f35-92c0-aa77c2933622/image.png" alt=""></p>
<p>star 아이콘이 들어간 버튼이 생성된다.</p>
<hr>
<h1 id="appbar">Appbar</h1>
<h2 id="title">title</h2>
<pre><code class="language-dart">return MaterialApp(
      home: Scaffold(
        appBar: AppBar( title: Text(&#39;타이틀&#39;),

        ),
        body: SizedBox(),
      )
    );</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/957fb6ec-eddd-405e-a170-781cfda56310/image.png" alt=""></p>
<p>여태 사용한 것처럼 appBar에 title을 입력하면 왼쪽 위에 작성되는 타이틀을 만들 수 있다.</p>
<hr>
<h2 id="leading">leading</h2>
<pre><code class="language-dart">appBar: AppBar( leading: Icon(Icons.star),

        )</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/c87ab382-2378-4684-847c-cd13a5e018ac/image.png" alt=""></p>
<p>leading을 사용하면 좌측 위에 아이콘을 입력할 수 있다.</p>
<p>보통 메뉴버튼으로 많이 사용한다.</p>
<pre><code class="language-dart">appBar: AppBar( leading: Icon(Icons.star), title: Text(&#39;타이틀&#39;),
        ),</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/921d6d57-9a38-45f2-a9b7-2e2759dd5506/image.png" alt=""></p>
<p>이렇게 타이틀과 같이 사용할 수 있다.</p>
<hr>
<h2 id="actions">actions</h2>
<pre><code class="language-dart">appBar: AppBar( actions: [Icon(Icons.star), Icon(Icons.star)], title: Text(&#39;타이틀&#39;),</code></pre>
<p>우측에 표시하는 actions 파라미터를 사용하였다. 배열로 여러가지를 추가할 수 있다.</p>
<p>Icon.star을 두 개 넣어보면</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/a92c240f-2c27-4adf-a01a-7b0fc988fd0c/image.png" alt=""></p>
<p>이렇게 우측 상단에 별 두 개가 출력되는 것을 확인할 수 있다.</p>
<p>출처 <a href="https://www.youtube.com/@codingapple">코딩애플</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Flutter - 박스 디자인]]></title>
            <link>https://velog.io/@nawhes_joo/Flutter-%EB%B0%95%EC%8A%A4-%EB%94%94%EC%9E%90%EC%9D%B8</link>
            <guid>https://velog.io/@nawhes_joo/Flutter-%EB%B0%95%EC%8A%A4-%EB%94%94%EC%9E%90%EC%9D%B8</guid>
            <pubDate>Sun, 14 Jul 2024 07:36:52 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>모든 UI의 기원은 네모박스다. 박스를 잘 그려아한다.</p>
</blockquote>
<h1 id="박스">박스</h1>
<pre><code class="language-dart">    return MaterialApp(
      home: Scaffold(
        appBar: AppBar( title: Text(&#39;앱임&#39;)),
        body: Container(
          width: 50, height: 50, color: Colors.blue,
        ),
      )
    );</code></pre>
<p>body에 Container를 사용하여 파란색 상자를 하나 생성하자.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/85d04db3-ce04-41f7-8b04-248c9ad05643/image.png" alt=""></p>
<p>body의 좌측 상단에 파란색 상자가 하나 생성되었다.</p>
<p>여백을 주자.</p>
<h1 id="여백">여백</h1>
<h2 id="margin">margin</h2>
<pre><code class="language-dart">    return MaterialApp(
      home: Scaffold(
        appBar: AppBar( title: Text(&#39;앱임&#39;)),
        body: Container(
          width: 50, height: 50, color: Colors.blue,
          margin: EdgeInsets.all(20),
        ),
      )
    );</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/51d7e1dc-adc2-4e12-858e-1130d550d6a5/image.png" alt=""></p>
<p>처음 생성한 파란 상자에 margin을 주자.</p>
<p>EdgeInsets를 사용하라고 한다.</p>
<p><code>EdgeInsets.all</code>을 사용하여 모든 면에 20의 바깥 여백을 주었다.</p>
<h2 id="padding">padding</h2>
<pre><code class="language-dart">    return MaterialApp(
      home: Scaffold(
        appBar: AppBar( title: Text(&#39;앱임&#39;)),
        body: Container(
          width: 50, height: 50, color: Colors.blue,
          padding: EdgeInsets.all(20),
        ),
      )
    );</code></pre>
<p>이번엔 margin 대신 padding을 주었다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/4a28f350-c081-4842-bd1c-8ca79d2a0c07/image.png" alt=""></p>
<p>padding을 주면 이런 식으로 보인다.</p>
<p>겉으로 보기에는 변한 게 없지만, 사실 안쪽 여백이 생긴 것이다.</p>
<pre><code class="language-dart">    return MaterialApp(
      home: Scaffold(
        appBar: AppBar( title: Text(&#39;앱임&#39;)),
        body: Container(
          width: 150, height: 50, color: Colors.blue,
          margin: EdgeInsets.all(20),
          child: Text(&#39;ddddd&#39;),
        ),
      )
    );</code></pre>
<p>상자의 width를 150으로 변경하고 Text를 넣어보자.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/3bf8f454-3f3f-46a5-b135-7e509449f02c/image.png" alt=""></p>
<p>padding 때문에 안쪽으로 밀려나 text가 짤려서 그려지는 것을 확인할 수 있다.</p>
<hr>
<h2 id="fromltrb">fromLTRB</h2>
<p>모든 면을 같은 사이즈로 주는 all 대신, 각각의 면에 사이즈를 개별로 줄 수 있는 fromLTRB 함수가 있다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/e109a8d3-7408-4209-aad6-fd633049f069/image.png" alt=""></p>
<p>순서대로 left, top, right, bottom에 원하는 사이즈를 입력하면 된다.</p>
<pre><code class="language-dart">margin: EdgeInsets.fromLTRB(0, 30, 0, 0)</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/a64bcbed-0533-4427-9da5-672ede63dbdb/image.png" alt=""></p>
<p>top만 30을 입력하였을 때의 결과물이다.</p>
<blockquote>
<p>margin은 매우 자주 사용하니 꼭 외워두자.</p>
</blockquote>
<hr>
<h1 id="decoration">decoration</h1>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/6002ddfe-224c-4fdd-964e-c8a8b3a0f554/image.png" alt=""></p>
<p>다른 옵션들은 decoration 안에서 작성해야 한다.</p>
<pre><code class="language-dart">    return MaterialApp(
      home: Scaffold(
        appBar: AppBar( title: Text(&#39;앱임&#39;)),
        body: Container(
          width: 50, height: 50, color: Colors.blue,
          decoration: BoxDecoration(
            border: Border.all(color: Colors.black)
          ),
        ),
      )
    );</code></pre>
<p>decoration 안에 border(테두리)를 입력하고 모든 면에 Colors.black을 작성하였다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/caf07c3b-9dbb-45c1-a37c-2a118886bb94/image.png" alt=""></p>
<p>저장하고 적용하면 위와 같은 오류가 발생한다.</p>
<p>그 이유는 Container 위젯의 decoration과 color 속성을 동시에 사용할 수 없기 때문이다.</p>
<p>아래는 수정된 코드이다.</p>
<pre><code class="language-dart">    return MaterialApp(
      home: Scaffold(
        appBar: AppBar( title: Text(&#39;앱임&#39;)),
        body: Container(
          width: 50, height: 50,
          decoration: BoxDecoration(
            border: Border.all(color: Colors.black)
          ),
        ),
      )
    );</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/18859e51-0a25-4d1f-9f3b-ff47950da92d/image.png" alt=""></p>
<p>container의 Color를 제거하면 정상적으로 검정색 테두리가 생성된다.</p>
<p>border 외에도 borderRadius, boxShadow 등 decoration을 통해 작성할 수 있다.</p>
<p>테두리가 아닌 box에도 color를 적용하고 싶으면</p>
<pre><code class="language-dart">
decoration: BoxDecoration(
 color: Colors.blue,
 border: Border.all(color: Colors.black),
),</code></pre>
<p>이렇게 작성하면 된다.</p>
<hr>
<h1 id="박스-위치-정렬">박스 위치 정렬</h1>
<p>body에 바로 Container를 작성하였다.</p>
<pre><code class="language-dart">    return MaterialApp(
      home: Scaffold(
        appBar: AppBar( title: Text(&#39;앱임&#39;)),
        body: Align(
          child: Container(
            width: 150, height: 50, color: Colors.blue,
          ),
        )
      )
    );</code></pre>
<p>그 Container를 Align으로 감싸보자.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/be277b59-2f87-4768-86ed-1abf10df3dc6/image.png" alt=""></p>
<p>감싼 후, 위 사진처럼 여러가지의 Align을 적용시킬 수 있다.</p>
<pre><code class="language-dart">body: Align(
  alignment: Alignment.topCenter,
  child: Container(
    width: 150, height: 50, color: Colors.blue,
  ),
)</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/fe3a1b5b-e8e9-485f-86b6-29bf30c53204/image.png" alt=""></p>
<p>topCenter를 적용해보았다.</p>
<hr>
<p>박스를 만들다보면 가로로 꽉 채운 박스를 생성할 때가 있다.</p>
<pre><code class="language-dart">child: Container(
  width: double.infinity, height: 50, color: Colors.blue,
),</code></pre>
<p>그럴 땐 width 자리에 double.infinity를 작성하면 된다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/c2af12d1-4ff9-41d6-8e14-8266def688ca/image.png" alt=""></p>
<p>말 그대로 width는 부모 박스를 넘어가지 않는 무한이 된다.</p>
<p>출처 - <a href="https://www.youtube.com/@codingapple">코딩애플</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Flutter - 가로세로 배치와 Scaffold]]></title>
            <link>https://velog.io/@nawhes_joo/Flutter-%EA%B0%80%EB%A1%9C%EC%84%B8%EB%A1%9C-%EB%B0%B0%EC%B9%98%EC%99%80-Scaffold</link>
            <guid>https://velog.io/@nawhes_joo/Flutter-%EA%B0%80%EB%A1%9C%EC%84%B8%EB%A1%9C-%EB%B0%B0%EC%B9%98%EC%99%80-Scaffold</guid>
            <pubDate>Sun, 14 Jul 2024 06:45:20 GMT</pubDate>
            <description><![CDATA[<h1 id="materialapp">MaterialApp()</h1>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/4f178b93-34f5-47f2-a0d7-8f30cc562be2/image.png" alt=""></p>
<p><code>MaterialApp()</code>이라는 위젯이다.</p>
<p>이 위젯을 써놓고 시작하면, 구글이 제공하는 Material 테마를 이용하여 앱을 만들 수 있다.</p>
<ul>
<li><p>구글이 제공하는 유용한 UI들을 이용할 수 있다. (구글 스타일)</p>
</li>
<li><p>아이폰 기본 앱 같은 스타일을 이용하고 싶으면 <code>Cuptertino()</code>를 이용하면 된다.</p>
</li>
</ul>
<p><code>커스터마이징</code>을 하고 싶으면 MaterialApp()을 사용해야 한다.</p>
<hr>
<h1 id="scaffold">Scaffold</h1>
<p>Scaffold는 상중하로 나누어주는 위젯이다.</p>
<pre><code class="language-dart">class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(),                        // 상단에 들어갈 위젯
        body: Container(),                        // 중간에 들어갈 위젯
        bottomNavigationBar: BottomAppBar(),    // 하단에 들어갈 위젯
      )
    );
  }
}</code></pre>
<p>appBar : 상단
body : 중간(내용 영역)
BottomAppBar : 하단</p>
<p>Container 넣어도 되고, 미리 만들어진 위젯을 넣어도 된다.</p>
<pre><code class="language-dart">class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(),
        body: Container(),
        bottomNavigationBar: BottomAppBar( child: Text(&#39;dsdfs&#39;)),    
      )
    );
  }
}</code></pre>
<p>BottomAppBar에 child: Text(&#39;dsdfs&#39;)) 를 입력하면</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/0122be37-6f78-4bfb-9dcb-6a6b2ee680a7/image.png" alt=""></p>
<p>하단에 입력한 텍스트가 나온다.</p>
<p>이렇게 간단한 하단바를 생성할 수 있다.</p>
<hr>
<h1 id="배치">배치</h1>
<h2 id="가로로-배치하는-법-row">가로로 배치하는 법 (Row)</h2>
<pre><code class="language-dart">    return MaterialApp(
      home: Scaffold(
        body: Container(
          child: Icon(Icons.star),
        ),
      )
    );</code></pre>
<p>상단과 하단은 제외하고 body에 아이콘을 배치해보자.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/6bd05e20-c04b-4262-a37b-4a8077df03a4/image.png" alt=""></p>
<p>위 사진을 보면 왼쪽 위에 별 아이콘이 생긴다.</p>
<p>이 이유는 Scaffold가 기준점을 <code>왼쪽 위</code>로 잡아주기 때문이다.</p>
<hr>
<p>이제 여러 위젯을 가로로 배치해보자.</p>
<pre><code class="language-dart">    return MaterialApp(
      home: Scaffold(
        body: Row(
          children: [
            Icon(Icons.star),
            Icon(Icons.star),
          ],
        ),
      )
    );</code></pre>
<p>   body에 Container가 아닌 Row 위젯을 사용하고, child가 아닌 children이라는 파라미터를 사용해보자.</p>
<p>children 파라미터는 Array 형태로 입력하면 된다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/1ea5ddcb-e014-456f-b6dc-420bfde1011a/image.png" alt=""></p>
<p>왼쪽 위 기준으로 별 아이콘 두개를 가로로 배치할 수 있게 된다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/db597521-4606-48b8-8ca7-49b9dbb22bbf/image.png" alt=""></p>
<p>위 사진과 같이 밑줄이 거슬린다.</p>
<p>lint 때문이라고 한다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/a982bfa8-fe80-4a9f-bbdf-2179c8d300ca/image.png" alt=""></p>
<p>const를 추가하면 해결되지만, const 사용하기 싫은 사람은</p>
<p>analysis_options.yaml 파일을 열어서 rules 안에 아래 코드를 추가하자.</p>
<pre><code class="language-dart">prefer_const_literals_to_create_immutables : false
</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/13bb9274-9267-4435-a9f5-f1b965fe9568/image.png" alt=""></p>
<p>밑줄이 깔끔하게 사라진다.</p>
<hr>
<h2 id="세로로-배치하는-법-column">세로로 배치하는 법 (Column)</h2>
<p>세로로 배치하는 방법은 Row 대신 Column을 사용하면 된다.</p>
<pre><code class="language-dart">    return MaterialApp(
      home: Scaffold(
        body: Column(
          children: [
            Icon(Icons.star),
            Icon(Icons.star),
            Icon(Icons.star),
          ],
        ),
      )
    );</code></pre>
<p>Row 대신 Column을 사용하고, children 파라미터를 사용하며 Array 형식으로 별 아이콘 3개를 입력하였다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/fece5311-5a6a-4aff-a79e-1cb1dde61e11/image.png" alt=""></p>
<p>저장하면 세로로 별 아이콘 3개가 생성되는 것을 확인할 수 있다.</p>
<hr>
<h1 id="정렬">정렬</h1>
<h2 id="mainaxisalignment">mainAxisAlignment</h2>
<p>Row와 Column을 이용하여 가로, 세로로 배치하는 방법을 배워보았다.</p>
<p>Scaffold가 왼쪽 위를 기준으로 잡기 때문에 왼쪽 위에 배치된다. 이를 정렬해보자.</p>
<pre><code class="language-dart">    return MaterialApp(
      home: Scaffold(
        body: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Icon(Icons.star),
            Icon(Icons.star),
            Icon(Icons.star),
          ],
        ),
      )
    );</code></pre>
<p><code>mainAxisAlignment</code>를 사용해보자.</p>
<blockquote>
<p>mainAxisAlignment는 Row의 가로축을 정렬</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/1b8f65f4-516d-42d9-be22-0a46af7857a7/image.png" alt=""></p>
<p>MainAxisAlignment.center를 사용하여 중간에 정렬되는 것을 확인할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/62edc578-7ff5-4449-9831-b848e57de14b/image.png" alt=""></p>
<p>자동완성을 보면 더 많은 정렬이 있다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/fa0ed462-5779-4e9b-b9f6-b8aa79222379/image.png" alt=""></p>
<p>위 사진은 MainAxisAlignment.spaceEvenly이다. </p>
<p>CSS의 display : flex와 유사하다.</p>
<hr>
<pre><code class="language-dart">    return MaterialApp(
      home: Scaffold(
        body: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            Icon(Icons.star),
            Icon(Icons.star),
            Icon(Icons.star),
          ],
        ),
      )
    );</code></pre>
<p>이번엔 Row를 Column으로 변경해보자.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/4d6e3370-11ef-494a-9016-4d7e04795796/image.png" alt=""></p>
<p>이번엔 세로축을 기준으로 정렬한다.</p>
<blockquote>
<p>Row의 mainAxisAlignment은 가로축, Column의 mainAxisAlign은 세로축을 의미한다.
crossAxisAlignment는 mainAxisAlignment의 반대인 Row - 세로축, Column - 가로축을 의미한다.</p>
</blockquote>
<hr>
<h1 id="활용">활용</h1>
<pre><code class="language-dart">    return MaterialApp(
      home: Scaffold(
        appBar: AppBar( title: Text(&#39;앱임&#39;)),
        body: Text(&#39;안녕&#39;),
        bottomNavigationBar: BottomAppBar(
          child: SizedBox(
            height: 70,
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                Icon(Icons.phone),
                Icon(Icons.message),
                Icon(Icons.contact_page),
             ],
            ),
          ),
        ),
      )
    );</code></pre>
<p>위처럼 appBar, body, bottomNavigationBar를 모두 이용하여</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/774647af-b3f1-435d-a2b5-b11ad6d55c99/image.png" alt=""></p>
<p>이렇게 디자인을 할 수 있다.</p>
<p>출처 - <a href="https://www.youtube.com/@codingapple">코딩애플</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Flutter - APK 빌드]]></title>
            <link>https://velog.io/@nawhes_joo/Flutter-APK-%EB%B9%8C%EB%93%9C</link>
            <guid>https://velog.io/@nawhes_joo/Flutter-APK-%EB%B9%8C%EB%93%9C</guid>
            <pubDate>Sat, 29 Jun 2024 13:46:27 GMT</pubDate>
            <description><![CDATA[<p>디바이스와 연결하여 테스트 할 때 어떤 동작을 진행하는지 알아보자.</p>
<h1 id="빌드">빌드</h1>
<h2 id="빌드란">빌드란?</h2>
<blockquote>
<p>Android에서의 빌드는 개발자가 소스 코드를 작성한 후 앱 설치 파일 <code>APK</code>를 만들기까지의 실행 과정을 의미한다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/48ad10a3-cee0-4a41-ae4a-b6a4dc289b24/image.png" alt=""></p>
<p>Android는 기본적으로 Linux 커널 위에 여러 소프트웨어 스택이 쌓인 형태로 Linux의 빌드와 동일하다고 생각하면 된다.</p>
<ul>
<li>Linux에서의 빌드는 소스 코드를 기계어로 컴파일 한 후 사용되는 라이브러리와의 <code>Link</code>를 수행하여 실제 실행 파일로 만드는 과정을 의미한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/710858a0-e342-47b4-9d9d-d3326254c3b1/image.png" alt=""></p>
<hr>
<h2 id="빌드-도구">빌드 도구</h2>
<blockquote>
<p>빌드 도구는 외부 라이브러리 추가 및 업데이트 등의 설정 시간을 단축시킨다. 또한 테스트 실행 및 호환성 체크까지 진행한다.</p>
</blockquote>
<p>Android에서 사용되는 빌드 도구는 <code>maven</code>, <code>gradle</code> 등이 있으며 구글에서는 <code>gradle</code> 의 사용을 권장하고 있다.</p>
<p><code>gradle</code>내부의 빌드 스크립트를 작성하여</p>
<ul>
<li>앱의 의존성</li>
<li>라이브러리</li>
<li>리소스 파일</li>
<li>빌드 설정</li>
</ul>
<p>등을 진행한다.</p>
<hr>
<h2 id="빌드-진행">빌드 진행</h2>
<blockquote>
<p>빌드 프로세스는 <code>gradle</code> 빌드 도구가 수행한다. 빌드 프로세스는 앱의 소스 코드와 별도 스크립트를 결합하여 <code>APK</code> 파일을 생성한다.</p>
</blockquote>
<ol>
<li><p>코틀린 컴파일러는 <code>.kt</code> 파일/ 자바 컴파일러는 <code>.java</code> 파일을 <code>.class</code> 바이트코드파일로 변환한다.</p>
<blockquote>
<p>가상 머신(Virtual Machine)에서 실행되기 위한 코드다. 바이트코드는 소스 코드와 기계어(머신 코드)의 중간 형태로, 직접 실행 가능한 바이너리 코드보다 더 추상화된 형태를 가지고 있다.</p>
</blockquote>
</li>
<li><p><code>Android SDK</code>의 <code>dx</code> 도구를 사용하여 <code>.class</code> 파일들을 <code>.dex</code> 파일로 변환한다.
<img src="https://velog.velcdn.com/images/nawhes_joo/post/17ca3c56-1a4b-43a0-84ed-92eaab3defe9/image.png" alt=""></p>
<blockquote>
<ul>
<li><code>dx</code> : Android SDK에 포함된 유틸리티로, Java 바이트코드 파일(.class 파일)을 Dalvik 바이트코드 파일(.dex 파일)로 변환하는 역할을 한다.</li>
</ul>
</blockquote>
<ul>
<li><code>dex</code> : dex(Dalvik Executable 파일)은 Android 운영체제에서 실행하기 위한 바이트코드 파일이다. Dalvik 또는 ART 가상 머신에서 실행된다.</li>
</ul>
<ol>
<li>Dalvik 또는 ART에서 효율적으로 메모리를 관리</li>
<li>여러 .class 파일의 내용을 포함하여, 애플리케이션의 로딩 시간을 단축</li>
<li>실행 선능을 향상시키기 위해 다양한 최적화 적용</li>
</ol>
</li>
</ol>
<ol start="3">
<li><p>Android 리소스 패키징 도구(<code>aapt(Android Asset Packaging Tool)</code>)와 <code>gradle</code> 사용하여 리소스 파일 및 외부 라이브러리 모듈을 <code>.dex</code>파일과 함께 APK 파일로 패키징합니다.</p>
</li>
<li><p><code>APK</code> 파일은 서명되어야 Android 디바이스에서 실행될 수 있다. <code>APK</code> 파일에 서명하기 위해서는 디지털 인증서를 사용해야 한다. <code>APK</code> 파일에 서명하는 작업은 빌드 과정에서 <code>gradle</code>에 설정된 값에 따라 자동으로 수행한다.</p>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/1d395883-3f99-41ea-bd71-f8f8269c67a4/image.png" alt=""></p>
<p>이렇게 서명된 <code>APK</code>가 만들어질 수 있다.</p>
<hr>
<h2 id="apk-설치-및-실행">APK 설치 및 실행</h2>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/ce58121b-63ae-4ac9-a022-8c1330021ec9/image.png" alt=""></p>
<ul>
<li><p>애플리케이션</p>
<ul>
<li>애플리케이션 : 사용자가 설치하고 실행하는 앱</li>
<li>앱 실행 : 사용자가 앱을 실행하는 단계</li>
</ul>
</li>
<li><p>애플리케이션 프레임워크</p>
<ul>
<li>애플리케이션이 동작하기 위해 필요한 기본 서비스와 클래스들이 포함된 계층. 개발자는 이 계층을 통해 시스템 자원과 서비스를 활용한다.  </li>
</ul>
</li>
<li><p>라이브러리 및 Android 런타임</p>
<ul>
<li>라이브러리 : 애플리케이션이 동작하기 위해 필요한 기본 서비스와 클래스들이 포함된 계층. 개발자는 이 계층을 통해 시스템 자원과 서비스를 활용한다.  </li>
<li>Android 런타임 : ART(Android Runtime)와 Dalvik 가상 머신이 포함되어 있다. 이 런타임 환경은 앱이 실행되는 동안 바이트코드를 실행할 수 있게 해준다.</li>
</ul>
</li>
<li><p>하드웨어 추상화 계층 (HAL)  </p>
<ul>
<li>하드웨어와 소프트웨어 간의 인터페이스를 제공한다. HAL을 통해 Android 시스템이 다양한 하드웨어 기능을 제어할 수 있다.</li>
</ul>
</li>
<li><p>리눅스 운영체제 (커널)  </p>
<ul>
<li>Android의 기본 운영체제. 리눅스 커널은 메모리 관리, 프로세스 관리, 네트워크 스택, 드라이버 등 기본적인 시스템 기능을 제공한다.</li>
</ul>
</li>
<li><p>하드웨어</p>
<ul>
<li>Android 기기의 물리적인 구성 요소. CPU, GPU, 메모리, 센서 등 다양한 하드웨어가 포함된다.  </li>
</ul>
</li>
<li><p>APK와 실행파일</p>
<ul>
<li>설치 파일(APK) : Android 애플리케이션 패키지 파일로, 앱을 배포하고 설치하기 위한 파일 형식이다.</li>
<li>AOT (Ahead-Of-Time) : 설치 시점에 APK를 실행 파일로 변환하는 컴파일 방법이다. 이 방식은 애플리케이션이 실행될 때 성능을 향상시킨다.</li>
<li>JIT(Just-In-Time) : 실행 시점에 바이트코드를 기계어로 변환하는 컴파일 방법다. 이 방식은 애플리케이션의 실행 속도를 최적화한다.</li>
<li>실행파일 : APK 파일이 AOT 또는 JIT 컴파일을 통해 변환된 파일로, 실제로 Android 기기에서 실행되는 파일이다.</li>
</ul>
</li>
</ul>
<p>설치된 <code>APK</code>는 안드로이드 런타임과정을 따라 초기 <code>JIT</code> 방식을 활용하여 앱을 설치한 후, 이후 자주 사용하는 앱을 <code>AOT</code> 방식을 활용하여 컴파일하는 방식으로 진행된다.</p>
<hr>
<h1 id="스마트폰과-연결-usb">스마트폰과 연결 (USB)</h1>
<ol>
<li><p>PC에 USB로 스마트폰과 연결한다.</p>
</li>
<li><p>스마트폰 설정 메뉴에서 <code>빌드 번호</code>를 검색한다.</p>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/46fe2792-49af-4c56-bd2c-741361d1441c/image.png" alt=""></p>
<ol start="3">
<li>빌드번호를 연속으로 클릭</li>
</ol>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/8b8a6c57-d819-49ca-bbf5-cf3f8088cd81/image.png" alt=""></p>
<ol start="4">
<li>설정에 개발자 옵션이 추가되고, 개발자 옵션에서 <code>USB 디버깅</code>을 활성화</li>
</ol>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/9d641d70-bb55-4fa2-b344-1879239e2190/image.png" alt=""></p>
<ol start="5">
<li>flutter doctor 명령을 입력하면 연결된 장치 갯수 표시됨</li>
</ol>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/e43930de-5c2e-42dd-a6cd-c2c8288cc9b1/image.png" alt=""></p>
<ol start="6">
<li>안드로이드 스튜디오에서 연결한 기기를 선택 후 실행버튼 클릭</li>
</ol>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/cfb2e131-485c-4efa-8a4e-19053fb1a52c/image.png" alt=""></p>
<ol start="7">
<li>실제 기기에서 플러터 데모 앱 실행 완료</li>
</ol>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/aded83b1-90a0-4b8e-be03-240c00da08a3/image.png" alt=""></p>
<hr>
<h1 id="워치와-연결">워치와 연결</h1>
<ol>
<li><p>워치 개발자 옵션 활성화</p>
<h5>1. 갤럭시 워치 설정 메뉴 이동
<h5>2. 워치 정보 선택
<h5>3. 소프트웨어 선택
<h5>4. 소프트웨어 버전 선택
<h5>5. 소프트웨어 버전 3번 연타 <개발자 모드를 켰습니다.> 팝업 확인
<h5>6. 설정 메뉴에 개발자 옵션이 생겼다면 개발자 모드 활성화 완료
</li>
<li><p>개발자 옵션에서 &quot;무선 디버깅&quot; 선택하여 활성화 (와이파이 연결 필수)
<img src="https://velog.velcdn.com/images/nawhes_joo/post/b6ef865d-8385-4926-99d5-3a6f2fd323c9/image.png" alt=""></p>
</li>
</ol>
<p>   <img src="https://velog.velcdn.com/images/nawhes_joo/post/e0398d5e-33a3-470c-be69-dc4d3f3b24f0/image.png" alt=""></p>
<ol start="3">
<li><p>&quot;+ 새 기기 등록&quot; 선택</p>
<p> <img src="https://velog.velcdn.com/images/nawhes_joo/post/e96c1d6a-687b-40eb-bf73-738c68b08c08/image.png" alt=""></p>
</li>
</ol>
<ol start="4">
<li>IP주소 및 포트 부분 (예 192.1xx.0xx.xxxxx) / 페어링 번호 확인<blockquote>
<p>워치 화면이 꺼지거나 와이파이 환경이 재접속 될 경우 포트값과 페어링 번호가 달라질 수 있으니 화면이 꺼지지 않도록 주의</p>
</blockquote>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/f8c9ae55-f1b4-440b-82f7-74e7ed24122c/image.png" alt=""></p>
<ol start="5">
<li><p>윈도우 - 실행 - cmd (.../Sdk/platform-tools) 또는 Android Studio 터미널에서</p>
<pre><code class="language-cmd">adb pair IP주소 및 포트 (예 192.1xx.0xx.xxxxx)</code></pre>
<p>엔터 입력 후 &quot;Enter pairing code:&quot; 에 페어링 코드 확인 후 입력</p>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/45fabb4d-289a-49a1-b526-2667bff89112/image.png" alt=""></p>
<ol start="6">
<li><p>워치에서 페어링 확인</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/983133ca-dcb2-4fc4-ba3a-172324c40ab0/image.png" alt=""></p>
</li>
</ol>
<p>출처 - <a href="https://minuhome.tistory.com/6188">minu님 블로그</a></p>
<hr>
<ol>
<li><p><strong>APK 파일 생성 및 전송</strong></p>
<ul>
<li><code>flutter run</code> 명령어를 사용하여 Flutter 프로젝트를 빌드하고 APK 파일을 생성한다.</li>
<li>이 APK 파일은 컴퓨터의 파일 시스템에서 생성되며, 이후 ADB(Android Debug Bridge)를 사용하여 USB로 연결된 Android 기기로 전송된다.</li>
</ul>
<ol start="2">
<li><strong>ADB를 통한 설치 및 실행</strong> <ul>
<li>APK 파일이 Android 기기로 전송된 후, ADB를 통해 Android 기기에 설치된다. ADB는 Android SDK의 일부로, 개발자가 Android 기기와 컴퓨터 사이에서 파일 전송 및 명령 실행을 할 수 있도록 지원한다.</li>
<li>설치가 완료되면, Android 기기에서 APK 파일이 실행되어 Flutter 앱이 시작된다.</li>
</ul>
</li>
</ol>
</li>
<li><p><strong>Flutter 앱 실행</strong></p>
<ul>
<li>Android 기기에서 Flutter 앱이 실행되면, 먼저 Flutter 엔진이 초기화된다. 이 과정에서 Skia 그래픽 엔진과 Dart VM이 활성화된다.</li>
<li>Dart VM이 초기화되면 Flutter 프로젝트의 Dart 코드가 Dart VM에서 실행된다. 이 코드는 Flutter의 위젯을 생성하고, 각 위젯의 상태를 관리하여 UI를 렌더링한다.</li>
<li>Flutter 엔진은 Skia를 사용하여 Dart 코드로 정의된 위젯 트리를 화면에 그린다. Skia 네이티브 그래픽 API를 호출하여 Android 기기의 실제 화면에 UI를 렌더링한다.</li>
</ul>
</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[Flutter - Widget(위젯)]]></title>
            <link>https://velog.io/@nawhes_joo/Flutter-Widget%EC%9C%84%EC%A0%AF</link>
            <guid>https://velog.io/@nawhes_joo/Flutter-Widget%EC%9C%84%EC%A0%AF</guid>
            <pubDate>Wed, 26 Jun 2024 04:37:46 GMT</pubDate>
            <description><![CDATA[<p>기본 위젯 4개만 알면 기초 끝이라고 한다.</p>
<p>우선 시작하기 전, analysis_options.yaml 파일을 수정하자</p>
<h1 id="setting">Setting</h1>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/7ccf6e80-6a5e-4ac9-82ab-e9ac967c9def/image.png" alt=""></p>
<p>analysis_options.yaml 파일을 열어보면, 이렇게 세팅되어 있을 것이다.</p>
<p>rules 란에 아래 코드를 추가하자.</p>
<pre><code class="language-yaml">rules:
  # 스페이스바 2개
  prefer_typing_uninitialized_variables : false
  prefer_const_constructors_in_immutables : false
  prefer_const_constructors : false
  avoid_print : false</code></pre>
<p>각각 어떤 규칙인지 알아보자.</p>
<ol>
<li><p><code>prefer_typing_uninitialized_variables</code></p>
<ul>
<li><p>설명 : 초기화되지 않은 변수에 명시적으로 타입을 지정하도록 권장한다.</p>
</li>
<li><p>예시</p>
<pre><code class="language-dart">// 권장되지 않음
var foo;

// 권장됨
String foo;</code></pre>
</li>
</ul>
</li>
<li><p><code>prefer_const_constructors_in_immutables</code></p>
<ul>
<li><p>설명 : 불변 클래스(immutable class)에서 생성자를 상수 생성자로 만들도록 권장한다.</p>
</li>
<li><p>예시</p>
<pre><code class="language-dart">class MyWidget extends StatelessWidget {
  // 권장되지 않음
  MyWidget();

  // 권장됨
  const MyWidget();
}</code></pre>
</li>
</ul>
</li>
<li><p><code>prefer_const_constructors</code></p>
<ul>
<li><p>설명 : 가능한 곳에서는 항상 상수 생성자를 사용하도록 권장한다.</p>
</li>
<li><p>예시</p>
<pre><code class="language-dart">// 권장되지 않음
var foo = MyClass();

// 권장됨
var foo = const MyClass();</code></pre>
</li>
</ul>
</li>
<li><p><code>avoid_print</code></p>
<ul>
<li><p>설명 : <code>print()</code> 함수 대신에 로깅(logging) 패키지를 사용하도록 권장한다.</p>
<pre><code>  `print()`는 디버깅 목적으로만 사용되며, 프로덕션 코드에서는 적절한 로깅 프레임워크를 사용하는 것이 좋다.</code></pre></li>
<li><p>예시</p>
<pre><code class="language-dart">// 권장되지 않음
print(&#39;Hello, World!&#39;);

// 권장됨
final logger = Logger(&#39;MyLogger&#39;);
logger.info(&#39;Hello, World!&#39;);</code></pre>
</li>
</ul>
</li>
</ol>
<p>모두 false로 설정하면 Dart 분석기가 위의 네 가지 규칙을 검사하지 않게 된다.</p>
<hr>
<h1 id="main">main</h1>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/f45cb255-d6d3-4911-b0d7-c3d00aea4d0f/image.png" alt=""></p>
<p>main.dart를 열어보면, 길게 세팅되어 있다.</p>
<p>구글이 이해하기 쉽도록 쉬운 프로젝트 하나를 생성해주었다.</p>
<p>void main() 밑은 싹~ 다 지우자.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/b9f5b4fe-e174-44cc-8d6c-984784ff9567/image.png" alt=""></p>
<p>stless를 입력한 뒤 tab 키를 누르면</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/da9a1a6b-0756-4fee-90f6-7e83869a0506/image.png" alt=""></p>
<p>이런 식으로 자동완성이 된다.</p>
<p>MyApp을 입력해주면 아래와 같이 될 것이다.</p>
<pre><code class="language-dart">import &#39;package:flutter/material.dart&#39;; // 다른 파일 or package import

void main() {
  runApp(const MyApp());    // runApp : app 구동. MyApp 자리에 앱 메인페이지 입력
}

class MyApp extends StatelessWidget {    // 앱 메인페이지. 기본적으로 채워야하는 문법
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {

    return MaterialApp( // 실질적 코드 작성
      home: 
    );

  }
}</code></pre>
<p>이러면 메인페이지 세팅은 끝이다.</p>
<p>앱 디자인을 넣는 법을 배워보자.</p>
<hr>
<h1 id="design">Design</h1>
<p>우리가 꼭 알아야할 위젯은 4가지이다.</p>
<ol>
<li>글자 (Text)</li>
<li>아이콘(Icon)</li>
<li>이미지(Image)</li>
<li>박스(Container)</li>
</ol>
<p>이 4가지만 알아도 웬만한 건 다 만들 수 있다.</p>
<p>Flutter에서 디자인하는 방법은, Widget 짜집기하는 식으로 디자인한다고 보면 된다.</p>
<p>글자를 넣고 싶으면 글자 위젯, 박스를 넣고 싶으면 박스 위젯을 넣어서 짜집기 하면 된다.</p>
<p>Widget의 개념이 매우 중요하다.</p>
<h2 id="text">Text</h2>
<p>우선 글자 먼저 넣어보자.</p>
<blockquote>
<p><strong>Text(&#39;글자&#39;)</strong></p>
</blockquote>
<pre><code class="language-dart">return MaterialApp(
    home: Text(&#39;Hello, World!&#39;)
);</code></pre>
<p>위 코드처럼 수정한 후 </p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/84f4a333-5ee4-4e1c-a55f-d87ce4b86691/image.png" alt=""></p>
<p>Chrome으로 main.dart를 실행해보자.</p>
<p>사실은 앱이지만 웹 브라우저로 미리보기를 띄우는 것이다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/764c49c6-73b2-4667-b6a9-f274237bc2a9/image.png" alt=""></p>
<p>크롬창에서 Hello, World!가 출력된다.</p>
<hr>
<h2 id="icon">Icon</h2>
<blockquote>
<p><strong>Icon(Icons.아이콘 이름)</strong></p>
</blockquote>
<pre><code class="language-dart">return MaterialApp(
    home: Icon(Icons.star)
);</code></pre>
<p>Icons.star를 집어넣어보자.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/dfb3ea03-036b-413c-9888-75d57d19ecae/image.png" alt=""></p>
<p>별 아이콘 하나가 생기는 것을 확인할 수 있다.</p>
<hr>
<pre><code class="language-dart">return MaterialApp(
    home: Icon(Icons.shop)
);</code></pre>
<p>이번엔 star 대신 shop을 입력하였다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/1b999915-4d48-46b4-a578-9aefb602895e/image.png" alt=""></p>
<p>별 대신 상점 아이콘이 출력되는 것을 확인할 수 있다.</p>
<blockquote>
<p>아이콘 이름은</p>
</blockquote>
<p><a href="https://fontawesomeicons.com/materialdesign/icons?search=list">https://fontawesomeicons.com/materialdesign/icons?search=list</a></p>
<blockquote>
</blockquote>
<p>이 사이트에서 검색해서 확인할 수 있다.</p>
<hr>
<h2 id="image">Image</h2>
<blockquote>
<p><strong>Image.asset(&#39;경로&#39;)</strong></p>
</blockquote>
<p>이미지를 넣고 싶으면 프로젝트 폴더 안에 존재해야 한다.</p>
<h3 id="이미지-등록">이미지 등록</h3>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/86e14784-04d2-4ebb-9d68-270b2e886d75/image.png" alt=""></p>
<p>프로젝트 우클릭 - New - Directory를 클릭하여 이미지 보관용 assets 폴더를 만들고 이미지를 넣는다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/e54c19fd-3763-4c6b-b505-4dae8ea31c20/image.png" alt=""></p>
<p>예시로 Flutter 이미지를 넣어두었다.</p>
<hr>
<h3 id="pubspec-수정">pubspec 수정</h3>
<p>그 다음, <code>pubspec.yaml</code> 파일을 열어보자.</p>
<blockquote>
<p>pubspec.yaml이란?
앱을 만들 때 필요한 모든 자료들을 쭉~ 정리한 파일이라고 보면 된다. 외부 패키지, 라이브러리 등등</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/3eaa9080-2e8b-4a6f-9980-08effb354c2d/image.png" alt=""></p>
<p>pubspec.yaml 파일에서 flutter를 찾아보자.</p>
<p>flutter를 찾았으면</p>
<pre><code class="language-yaml">flutter:
  assets:
    - assets/</code></pre>
<p>위 처럼 수정해주자.</p>
<p>assets 안에 있는 모든 이미지 파일을 가져다 쓸 수 있다.</p>
<hr>
<h3 id="호출">호출</h3>
<p>이제 main.dart에서 호출해보자.</p>
<pre><code class="language-dart">return MaterialApp(
  home: Image.asset(&#39;flutter.png&#39;)
);</code></pre>
<p>assets 폴더에 이미지를 추가하고, pubspec.yaml 파일을 정상적으로 수정했다면</p>
<p>Image.asset(&#39;경로&#39;)에서 경로 위치에 이미지 파일명만 입력하면 된다.</p>
<p>(정확한 경로 명은 &#39;assets/flutter.png&#39;이다.)</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/fe7bac57-5881-4b78-8069-259b2eea7134/image.png" alt=""></p>
<hr>
<h2 id="container--sizedbox">Container / SizedBox</h2>
<blockquote>
<p><strong>Container( 스타일(스타일 명 : 값) ) / SizedBox()</strong></p>
</blockquote>
<pre><code class="language-dart">return MaterialApp(
    home: Container( width: 50, height: 50, color: Colors.blue )
);</code></pre>
<blockquote>
<p>플러터의 사이즈의 단위는 LP이다.
50LP == 1.2cm</p>
</blockquote>
<p>위 코드처럼 Container()를 입력하고 테스트해보자.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/0835040a-16f9-43d4-b099-e467c4eabb32/image.png" alt=""></p>
<p>엄청난 크기의 파란색 박스가 생성되었다.</p>
<p>50을 입력했는데 왜 이렇게 크게 나올까??</p>
<p>1.2cm라고 해도 너무 크다.</p>
<p>그 이유는 무엇일까?</p>
<p><code>어디부터 50을 차지할지 몰라서 그렇다</code></p>
<p>어디부터 차지할지는 <code>부모</code>가 정한다.</p>
<pre><code class="language-dart">return MaterialApp(
  home: Center( // 정중앙
    child: Container( width: 50, height: 50, color: Colors.blue ) // 자식
  )
);</code></pre>
<p>위 코드처럼 수정해보자.</p>
<p>Center를 통해 기준점을 중앙으로 설정해주고, child 안에 자식(Container)를 작성해주면</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/2a91a930-e613-4098-b7f8-e36ab8837752/image.png" alt=""></p>
<p>50LP * 50LP의 파란색 박스가 생성된다.</p>
<p>출처 - <a href="https://www.youtube.com/@codingapple">코딩애플</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Flutter 개발환경 구축]]></title>
            <link>https://velog.io/@nawhes_joo/Flutter-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95-%EC%A4%91%EA%B0%84%EC%A0%80%EC%9E%A5</link>
            <guid>https://velog.io/@nawhes_joo/Flutter-%EA%B0%9C%EB%B0%9C%ED%99%98%EA%B2%BD-%EA%B5%AC%EC%B6%95-%EC%A4%91%EA%B0%84%EC%A0%80%EC%9E%A5</guid>
            <pubDate>Fri, 21 Jun 2024 08:40:40 GMT</pubDate>
            <description><![CDATA[<h1 id="구축-방법-종류">구축 방법 종류</h1>
<p>Flutter 개발환경을 구축하는 방법에는 여러가지가 있다.</p>
<ol>
<li><p>Flutter 공식 문서에서 제공하는 설치 가이드 (Flutter SDK)</p>
</li>
<li><p>명령줄 도구를 이용한 설치(CLI)</p>
</li>
<li><p>패키지 매니저를 통한 설치</p>
</li>
<li><p>IDE 통합 개발환경 설치 (Visual Studio Code, Android Studio)</p>
</li>
<li><p>Docker를 이용한 설치</p>
</li>
</ol>
<p>Flutter 공식 문서에서 설치하는 방법과 IDE 통합 개발환경 설치하는 방법만 간단히 비교해보자.</p>
<h1 id="비교">비교</h1>
<h2 id="장단점">장단점</h2>
<h3 id="flutter-sdk-수동-설치">Flutter SDK 수동 설치</h3>
<h4 id="장점">장점</h4>
<ol>
<li><p><strong>제어 및 유연성</strong>:</p>
<ul>
<li>개발 환경을 세밀하게 제어할 수 있다.</li>
<li>필요한 도구와 설정을 직접 관리할 수 있어, <code>커스터마이징이 용이</code>하다.</li>
</ul>
</li>
<li><p><strong>환경 독립성</strong>    </p>
<ul>
<li>IDE에 종속되지 않으므로, 다양한 텍스트 에디터나 IDE와 함께 사용할 수 있다.</li>
<li>특정 버전의 Flutter SDK를 유지하거나 <code>여러 버전을 동시에 관리</code>하기 쉽다.</li>
</ul>
</li>
<li><p><strong>경량성</strong></p>
<ul>
<li>불필요한 플러그인이나 확장 기능을 설치할 필요 없이, <code>최소한의 도구</code>만 설치하여 사용 가능</li>
</ul>
</li>
</ol>
<h4 id="단점">단점</h4>
<ol>
<li><p><strong>설정 복잡성</strong></p>
<ul>
<li>환경 변수를 직접 설정해야 하므로, 설정 과정이 다소 복잡할 수 있다.</li>
<li>의존성 설치 및 관리가 <code>수동</code>으로 이루어져야 한다.</li>
</ul>
</li>
<li><p><strong>개발 생산성</strong></p>
<ul>
<li>IDE의 자동화된 기능(자동 완성, 디버깅 등)을 완전히 활용하기 어렵다.</li>
<li>빌드 및 실행 등의 작업이 <code>수동</code>으로 이루어져 시간이 더 소요될 수 있다.</li>
</ul>
</li>
</ol>
<hr>
<h3 id="ide를-통한-설치visual-studio-code-android-studio">IDE를 통한 설치(Visual Studio Code, Android Studio)</h3>
<h4 id="장점-1">장점</h4>
<ol>
<li><p><strong>설정 간편성</strong></p>
<ul>
<li>플러그인을 통해 Flutter SDK를 쉽게 설치하고 설정할 수 있다.</li>
<li>환경 변수를 자동으로 설정해주므로, 개발환경 구성이 간편하다.</li>
</ul>
</li>
<li><p><strong>개발 생산성</strong>   </p>
<ul>
<li>자동 완성, 코드 포맷팅, 상태 검사 등 다양한 개발 도구를 제공하여 <code>생산성</code>을 높인다.</li>
<li><code>통합된 UI</code>로 다양한 작업(프로젝트 생성, 빌드, 실행 등)을 쉽게 수행할 수 있다.</li>
</ul>
</li>
<li><p><strong>통합 기능</strong></p>
<ul>
<li>Git과 같은 버전 관리 도구와 쉽게 통합할 수 있다.</li>
<li>프로젝트 관리를 위한 다양한 확장 기능을 지원한다.</li>
</ul>
</li>
</ol>
<h4 id="단점-1">단점</h4>
<ol>
<li><p><strong>의존성</strong></p>
<ul>
<li>특정 IDE에 종속될 수 있어, 다른 환경에서 작업할 때 <code>비효율적</code>일 수 있다.</li>
<li>플러그인이나 IDE 버전에 따라 <code>호환성 문제</code>가 발생할 수 있다.</li>
</ul>
</li>
<li><p><strong>리소스 사용</strong>    </p>
<ul>
<li>IDE가 더 많은 시스템 자원을 사용하므로, 시스템 사양이 낮을 경우 <code>성능 저하</code>가 발생할 수 있다.</li>
<li>불필요한 기능이나 플러그인이 설치되어 있을 경우, IDE가 무거워질 수 있다.</li>
</ul>
</li>
</ol>
<hr>
<h2 id="개발할-때의-차이점">개발할 때의 차이점</h2>
<h3 id="flutter-sdk-수동-설치-1">Flutter SDK 수동 설치</h3>
<ol>
<li><p><strong>프로젝트 생성 및 관리</strong></p>
<ul>
<li><code>명령줄(CLI)</code>을 통해 프로젝트를 생성하고 관리한다. ( 예 : &#39;flutter create my_project&#39; )</li>
<li>IDE가 아닌 텍스트 에디터( 예 : Sublime Text, Vim )에서 코드를 작성할 수 있다.</li>
</ul>
</li>
<li><p><strong>빌드 및 실행</strong></p>
<ul>
<li><code>명령줄(CLI)</code>를 통해 앱을 빌드하고 실행한다. ( 예 : &#39;flutter run&#39; )</li>
<li>디버깅도 명령줄 도구를 통해 <code>수동</code>으로 진행한다.</li>
</ul>
</li>
<li><p><strong>의존성 관리</strong></p>
<ul>
<li>직접 <code>flutter doctor</code>를 실행하여 필요한 의존성을 설치하고 관리한다.</li>
</ul>
</li>
</ol>
<hr>
<h3 id="ide를-통한-설치">IDE를 통한 설치</h3>
<ol>
<li><p><strong>프로젝트 생성 및 관리</strong></p>
<ul>
<li>IDE의 GUI를 통해 <code>쉽게</code> 프로젝트를 생성하고 관리할 수 있다.</li>
<li>프로젝트 설정, 디펜던시 관리, 코드 작성 등이 <code>통합된 인터페이스</code>에서 이루어진다.</li>
</ul>
</li>
<li><p><strong>빌드 및 실행</strong></p>
<ul>
<li>IDE내에서 <code>버튼 클릭만으로</code> 앱을 빌드하고 실행할 수 있다. <del>딸깍</del></li>
<li><code>디버깅 도구</code>를 사용하여 브레이크포인트 설정, 변수 검사 등이 가능하다.</li>
</ul>
</li>
<li><p><strong>의존성 관리</strong>    </p>
<ul>
<li>IDE가 <code>의존성 문제를 자동으로 감지</code>하고, 필요한 경우 설치를 안내해준다.</li>
</ul>
</li>
</ol>
<hr>
<h2 id="요약">요약</h2>
<ul>
<li><p><strong>Flutter SDK 수동 설치</strong>는 더 많은 <code>제어와 유연성</code>을 제공하지만, 설정과 관리가 복잡할 수 있으며, 개발 생산성이 다소 떨어질 수 있다.</p>
</li>
<li><p><strong>IDE를 통한 설치</strong>는 <code>설정이 간편</code>하고, 다양한 <code>자동화 도구</code>와 <code>통합 기능</code>을 제공하여 개발 생산성을 크게 향상시킬 수 있다. 다만, 특정 IDE에 종속될 수 있고, 시스템 자원을 더 많이 사용할 수 있다.</p>
</li>
</ul>
<p><br><br/>
이렇게 장단점이 뚜렷하다. 이 장점들을 한 번에 다 사용할 순 없을까? </p>
<p>가능하다. 쓰까서 쓰면된다.</p>
<hr>
<h1 id="개발환경-구축">개발환경 구축</h1>
<h2 id="flutter-sdk-설치">Flutter SDK 설치</h2>
<ol>
<li><a href="https://flutter.dev">Flutter 웹사이트</a>에 접속한 뒤 우측 상단의 &#39;Get Start&#39; 클릭</li>
</ol>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/02db61b6-1054-453c-8b45-ada176a6620d/image.png" alt=""></p>
<blockquote>
<p>플러터를 구동하여 개발할 수 있는 운영체제(32비트 운영체제는 정상적으로 동작하지 않음)</p>
</blockquote>
<ul>
<li>윈도우 7 SP1 이상(64비트)</li>
<li>맥OS</li>
<li>리눅스</li>
</ul>
<ol start="2">
<li>PC에 설치된 운영체제에 맞는 항목 선택</li>
</ol>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/44b9ea32-d815-49c8-863b-f81a6e3941d4/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/e6ff59fa-2e87-4e46-acd1-48dd94f3b7fd/image.png" alt=""></p>
<p>일단 최신버전을 다운받았다.</p>
<ol start="3">
<li>다운로드 받은 파일을 C:/src 폴더에 압축해제</li>
</ol>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/41c8adf7-30c7-4748-9e3d-f50f53f5c72b/image.png" alt=""></p>
<blockquote>
<p>쓰기 권한, 업데이트 및 설치 문제 등의 이유 때문에 C:/ProgramFiles 와 같은 높은 권한이 필요한 위치는 피하는 것이 좋다.</p>
</blockquote>
<hr>
<h2 id="환경-변수-등록">환경 변수 등록</h2>
<ul>
<li>Flutter 명령 파일은 압축을 푼 폴더 아래 bin 폴더에 위치</li>
<li>이 파일이 들어있는 경로를 운영체제의 환경변수에 등록하면 어디에서나 플러터 명령을 사용 가능</li>
</ul>
<ol>
<li><p>&#39;검색&#39;에 &#39;환경 변수&#39;를 입력하고 시스템 환경 변수 편집&#39;을 클릭</p>
<p> <img src="https://velog.velcdn.com/images/nawhes_joo/post/23937593-1316-4400-b5c3-50b871696689/image.png" alt=""></p>
</li>
<li><p>&#39;시스템 속성&#39; 창이 뜨면 &#39;고급&#39; 탭에서 &#39;환경 변수(N)&#39;을 클릭</p>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/f7c416a0-86ea-4edd-9032-0f303bef85b7/image.png" alt=""></p>
<ol start="3">
<li>환경 변수 창에서 &#39;Path&#39;를 선택한 후 &#39;편집&#39; 클릭</li>
</ol>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/2ea9dec5-064d-452b-a143-73fef22f940d/image.png" alt=""></p>
<ol start="4">
<li><p>&#39;찾아보기&#39;를 클릭하고 Flutter SDK의 bin 폴더 위치 ( C:/src/flutter/bin )를 선택하고 &#39;확인&#39; 클릭</p>
<p> <img src="https://velog.velcdn.com/images/nawhes_joo/post/d1d0fc4f-982a-4137-aa56-71a7a4badd8b/image.png" alt=""></p>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/0c879275-cbd6-4ea5-9ed3-63a78513405f/image.png" alt=""></p>
<ol start="5">
<li>터미널을 실행시킨 후 <code>flutter --version</code>을 입력</li>
</ol>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/dad952e0-8a46-483f-8fbd-5aac23c7f1ac/image.png" alt=""></p>
<ol start="6">
<li><code>flutter doctor</code>를 통해 SDK가 정상적으로 설치되었는지 확인</li>
</ol>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/4f695338-566a-4d99-8445-29c41da22323/image.png" alt=""></p>
<p>여러 항목들이 나오는데 SDK 설치 확인은 첫 번째 [v] Flutter 녹색 체크를 보면 된다.</p>
<p>나머지 사항은 개개인의 PC 환경에 따라 다르게 나올 수 있다.</p>
<p>위처럼 출력되면 성공</p>
<hr>
<h2 id="앱-개발-도구-설치">앱 개발 도구 설치</h2>
<ul>
<li><p>플러터 개발에 사용할 도구로 다음과 같은 IDE(통합 개발 환경)을 지원</p>
<ul>
<li><code>Android Studio</code> : <a href="https://developer.android.com/studio">https://developer.android.com/studio</a></li>
<li><code>Visual Studio Code</code> : <a href="https://code.visualstudio.com/">https://code.visualstudio.com/</a></li>
</ul>
</li>
<li><p>두 가지 IDE 모두 훌륭하지만 플러터는 구글이 만들었기 때문에 자사에서 개발한 안드로이드 스튜디오를 사용하는 것이 편의성 등에서 더 나음</p>
</li>
<li><p>설치 환경을 구성할 때에도 안드로이드 스튜디오가 더 간단함</p>
</li>
</ul>
<p>안드로이드 스튜디오로 진행할 것이다.</p>
<hr>
<h3 id="android-studio-설치">Android Studio 설치</h3>
<ol>
<li><a href="https://developer.android.com/studio">Android Studio 다운로드</a> / <a href="https://developer.android.com/studio/archive">Android Studio 구버전 다운로드</a> 
 <img src="https://velog.velcdn.com/images/nawhes_joo/post/ea67497b-6886-4d3c-858d-be064cac3b24/image.png" alt=""><blockquote>
<p>구버전 설치 시 우측 상단 언어 한국어 -&gt; ENGLISH로 변경</p>
<ul>
<li>Giraffe 버전이 플러터 개발을 위한 최신기능과 안정성을 제공한다고 함</li>
</ul>
</blockquote>
</li>
</ol>
<hr>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/2b551630-dd61-4bf6-bf6e-8fbbd1dc1ee9/image.png" alt=""></p>
<ol start="2">
<li><p>다운로드한 설치 파일을 실행하여 설치 진행</p>
</li>
<li><p>Flutter, Dart 플러그인 설치</p>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/02d04ffe-d0d2-45e7-8698-6ea22490e5b5/image.png" alt=""></p>
<p>설치가 완료되면 Restart IDE를 클릭하여 재실행해주자.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/7cc4aa26-a3f8-4cb2-a90c-adf50b34460b/image.png" alt=""></p>
<p>Installed를 들어가보면 Flutter와 Dart가 설치된 것을 확인할 수 있다.</p>
<ol start="4">
<li>Flutter Project 생성하기</li>
</ol>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/99c39d7a-3bf6-4873-9b0c-7bb8083c8665/image.png" alt=""></p>
<p>New Flutter Project를 클릭</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/41de945e-046f-409d-85fa-1b6791898756/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/8eec0a4e-85a2-400f-ab6d-8c2e44051db1/image.png" alt=""></p>
<p>Project name : 프로젝트 명
Project location : 프로젝트 위치
Description : 설명
Organization : 조직
Android language : 안드로이드 언어 설정
Platforms : 플랫폼</p>
<p>목적에 맞게 작성하자.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/51e3e7f7-309a-4b32-95b3-13aec5639547/image.png" alt=""></p>
<p>작성 후 Create를 누르면 프로젝트가 생성된다.</p>
<ol start="5">
<li>flutter doctor</li>
</ol>
<p>좌측 아래 터미널을 클릭하여 flutter doctor를 입력한다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/5720f7cd-17e4-4500-b3d7-5d1029cd7732/image.png" alt=""></p>
<p>위와 같은 오류가 발생한다.</p>
<ul>
<li><p>에러 1) cmdline-tools component is missing</p>
</li>
<li><p>에러 2) Android license status unknown</p>
<ul>
<li>이 경고는 toolchain이 연결되지 않았다는 오류, 라이센스를 허용하지 않았다는 뜻이다.</li>
</ul>
<hr>
<p>5-1. SDK Manager에서 Command-line Tools 설치</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/977630c4-0f2a-4702-8815-77aa111ba732/image.png" alt=""></p>
<p>우측 상단에 톱니바퀴 버튼을 누르고 SDK Manager를 클릭한다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/564139a4-422d-42f1-9fca-7769abf58bfb/image.png" alt=""></p>
<p>Android SDK Command-line Tools (latest)를 클릭 후 Apply 버튼을 눌러 설치 후 OK 버튼 클릭.</p>
<p>5-2. 환경 변수 설정</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/b8e171a4-1ca6-496f-a29c-491e13587b56/image.png" alt=""></p>
<p>시스템 변수 섹션에서 새로 만들기 클릭.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/e5d8a84b-e013-4fe0-9578-46d2523034a1/image.png" alt=""></p>
<p>변수 이름 : ANDROID_HOME
변수 값 : C:\Users\username\Appdata\Local\Android\Sdk</p>
<p>AppData는 기본적으로 숨김폴더이다. 보이지 않는다면 보기 설정에서 숨김폴더 보이도록 변경하면 된다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/486564b4-1a2e-4d92-8e51-503e81f6b8c7/image.png" alt=""></p>
<p>시스템 변수 섹션에서 Path 변수를 선택하고 편집을 클릭한다.</p>
<p>새로 만들기 or 찾아보기를 클릭하여</p>
<pre><code>C:\User\username\Appdata\Local\Android\Sdk\platform-tools
C:\User\username\Appdata\Local\Android\Sdk\cmdline-tools\latest\bin</code></pre><p>두 가지를 추가한다.</p>
<p>5-3 Android SDK 라이센스 동의</p>
<pre><code>flutter doctor --android-licenses</code></pre><p>Android Studio 하단의 Terminal에 위 명령어를 입력한다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/4ec2f2a4-e4f1-4473-a2f8-1bf5f7fb11e7/image.png" alt=""></p>
<p>위 사진처럼 뜬다면 Y를 눌러 라이센스를 검토할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/4fd24354-c93e-4200-aadc-2a897444eb64/image.png" alt=""></p>
<p>Y를 누르니 위와 같이 라이센스를 보여준다.</p>
<p>Y를 한 번 더 눌러서 동의해주자.</p>
<p>5개의 라이센스를 동의한 후 </p>
<pre><code>flutter doctor</code></pre><p>명령어를 입력하면</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/948c338b-57d4-4602-b307-62a0766e84df/image.png" alt=""></p>
<p>오류가 해결된 것을 확인할 수 있다.</p>
<p>VSCode는 사용하지 않을 것이니 굳이 해결하지 않아도 된다.</p>
<hr>
<h3 id="wear-os-개발-환경-설정">Wear OS 개발 환경 설정</h3>
<p>Android Studio에서 watch 앱을 개발하기 위해서 Wear OS를 필요로 하다.</p>
<p>(추후 공개)</p>
<hr>
<h2 id="테스트">테스트</h2>
<h3 id="가상-기기">가상 기기</h3>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/15b32ec0-adb5-4289-87b4-6e32a52ab116/image.png" alt=""></p>
<p>우측 Device Manager를 누른 후 Create Visual Device를 클릭</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/80b37c5d-9d15-46a9-ad9e-5b82b6e9bc5d/image.png" alt=""></p>
<p>원하는 기기를 선택한 후 Next</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/b806d8ce-c158-4be8-8a91-42dadd58b17d/image.png" alt=""></p>
<p>API 35가 기본으로 설정되어있다.</p>
<p>Tiramisu를 설치&amp;선택 후 Next</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/432fe8ca-9b5c-4cad-8dca-5903cf67082a/image.png" alt=""></p>
<p>따로 건드릴 건 없다.</p>
<p>Finish~</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/cd7634d4-27a5-46ac-b56a-dce01c7cbb0a/image.png" alt=""></p>
<p>상단에 no device selected를 누른 후 방금 생성한 device를 클릭</p>
<p>만약 보이지 않는다면 Refresh를 누르면 된다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/c00b0f95-6838-4487-be8a-cae620dc14ab/image.png" alt=""></p>
<p>우측 Running Device를 클릭하면 Virtual Device가 보인다.</p>
<p>위 사진처럼 에뮬레이터가 안드로이드 스튜디오 화면 내에서 실행이 된다.</p>
<p>이렇게 사용해도 되지만 불편하다면 따로 뺄 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/8a79c7ef-2689-42ab-8442-91d52cbf4cf9/image.png" alt=""></p>
<p>Settings - Emulator에 들어가 Launch in the Running Devices tool window 체크해제 후 OK를 누르고 Android Studio를 재실행</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/3a5d3569-239d-4f2f-9696-7a562a08953e/image.png" alt=""></p>
<p>이렇게 밖으로 뺴낼 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/384d7583-deae-427e-9ecb-6ba80df7661d/image.png" alt=""></p>
<p>상단에 초록색 화살표를 클릭하면 기본으로 세팅된 어플이 실행된다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/0d3f54c6-5e87-45de-a2d8-35c5ea2451cf/image.png" alt=""></p>
<p>프로젝트 생성 시 기본으로 생성된 어플을 실행시킬 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Flutter(플러터)란?]]></title>
            <link>https://velog.io/@nawhes_joo/Flutter%ED%94%8C%EB%9F%AC%ED%84%B0%EB%9E%80</link>
            <guid>https://velog.io/@nawhes_joo/Flutter%ED%94%8C%EB%9F%AC%ED%84%B0%EB%9E%80</guid>
            <pubDate>Sun, 16 Jun 2024 07:00:11 GMT</pubDate>
            <description><![CDATA[<h1 id="개요">개요</h1>
<p>Flutter는 Google에서 개발하고 관리하는 오픈 소스 모바일 앱(Application) 개발 프레임워크(Framework)이다. 이 프레임워크는 Android와 iOS 등 다양한 플랫폼에서 동작하는 앱을 개발하기 위해 사용되며, 모바일 앱(Application)의 개발 속도를 높이기 위한 다양한 기능과 도구를 제공한다.</p>
<hr>
<h1 id="특징">특징</h1>
<p>Flutter는 <code>다트(Dart)</code>라고 하는 언어를 사용해 개발한다. 구글이 멀티 플랫폼 상에서 동작되도록 하는 앱을 위해 디자인된 프로그래밍 언어이며, 2011년 10월에 공개되었다.</p>
<blockquote>
<p><strong><code>다트(Dart)</code>란?</strong> </p>
</blockquote>
<p><code>C언어 기반의 문법을 사용</code>하며, 자바, 자바스크립트, C#등의 언어에서 영감을 받아 개발되어 프로그래머들에게 자연스럽게 다가가는 Dart의 목적에 맞게, <strong>기존 프로그래밍 언어들의 특징들이 많이 보인다.</strong> 이것은 다른 언어를 경험해 본 유저라면 쉽게 접할 수 있는 장점이 된다.</p>
<p>Flutter의 UI 구성 요소는 <code>위젯</code>으로 이루어져 있다. 이 위젯들은 서로 다양하게 조합되어 복잡한 UI를 만들어낸다. Flutter에서는 <code>머티리얼(Material) 디자인 스타일</code>의 위젯과 <code>iOS 스타일</code>의 위젯을 기본적으로 제공하며, 커스터마이징이 가능한 다양한 위젯들도 제공한다.</p>
<blockquote>
<p>위젯(Widget)은 화면에 표시되는 모든 요소들을 말하며, 버튼, 텍스트 입력 필드, 이미지, 레이아웃을 구성하는 컨테이너 등이 이에 해당한다.</p>
</blockquote>
<p>Flutter의 가장 큰 장점 중 하나는 <code>빠른 개발 속도</code>이다. Flutter는 <code>핫 리로드(Hot Reload)</code>라는 기능을 제공한다.</p>
<p>핫 리로드(Hot Reload)를 사용하면 다음과 같은 장점이 있다.</p>
<blockquote>
<ol>
<li><strong>빠른 피드백</strong> : 코드를 수정한 후 바로 결과를 확인할 수 있어 개발 속도가 향상된다.</li>
<li><strong>상태 유지</strong> : 앱의 상태를 유지한 채로 변경 사항을 반영할 수 있어 개발 중인 기능을 더 쉽게 테스트할 수 있다.</li>
<li><strong>테스트 및 디버깅 용이성</strong> : 변경 사항이 즉시 반영되므로 빠르게 테스트하고 디버깅할 수 있다.</li>
</ol>
</blockquote>
<p>요약하면, Flutter는 크로스 플랫폼 모바일 앱 개발을 위한 프레임워크로, Dart 언어를 사용하며, 뛰어난 성능과 빠른 개발 주기를 제공한다고 볼 수 있다.</p>
<hr>
<h2 id="다트dart">다트(Dart)</h2>
<h3 id="dart란">Dart란?</h3>
<p>Dart 언어는 Google에서 개발한, 주로 모바일 애플리케이션 및 웹개발에 사용되는 언어로써 Dart는 특히 Flutter와 함께 사용될 때 강력한 기능을 발휘하며, 그 성능과 생산성면에서 주목받고 있다. 하지만 개발 커뮤니티와 라이브러리 생태계가 계속 성장해 나갈 것인지는 앞으로의 발전에 달려있다.</p>
<h3 id="특징-1">특징</h3>
<ol>
<li><p><strong>객체 지향 프로그래밍 (OOP)</strong><br>Dart는 객체 지향 프로그래밍 언어로, 클래스와 객체를 사용하여 모든 것을 모델링한다. 이로써 코드와의 구조화와 재사용성이 증가한다<br/></p>
</li>
<li><p><strong>가독성과 간결성</strong> <br>Dart는 C언어 기반의 문법을 사용하며, 자바, 자바스크립트, C# 등의 언어에서 영감을 받아 개발되었다. 이는 코드를 읽고 이해하기 쉽게 만든다.<br/></p>
</li>
<li><p><strong>타입 안정성</strong> <br>Dart는 정적 타입 언어로, 변수의 데이터 타입을 명시적으로 선언할 수 있다. 이는 코드 실행 전에 타입 에러를 찾아내는 데 도움이 된다.<br/></p>
</li>
<li><p><strong>멀티패러다임</strong> <br>Dart는 함수형 프로그래밍과 객체 지향 프로그래밍의 요소를 모두 포함하고 있어 다양한 프로그래밍 스타일을 지원한다.<br/></p>
</li>
<li><p><strong>컴파일러</strong> <br>Dart는 <code>JIT(Just-In-Time) 컴파일러</code>와 <code>AOT(Ahead-Of-Time) 컴파일러</code>를 모두 지원하여 개발자가 선택적으로 성능을 최적화할 수 있다.<br/>
<br><br/></p>
</li>
</ol>
<h4 id="jit-컴파일러-just-in-time">JIT 컴파일러 (Just-In-Time)</h4>
<ul>
<li><p><strong>특징</strong> : Dart JIT 컴파일러는 프로그램 실행 중에 코드를 컴파일한다.</p>
</li>
<li><p><strong>장점</strong></p>
<ul>
<li><strong>빠른 개발 속도</strong> : 코드 변경 후 즉시 실행이 가능하여, 빠른 피드백을 받을 수 있다. 이로 인해 개발 및 디버깅 과정에서 매우 유용하다.</li>
<li><strong>동적 성능 최적화</strong> : 실행 중에 프로그램의 실행 패턴을 분석하고 최적화를 적용할 수 있어, 애플리케이션이 실행될수록 성능이 개선될 수 있다.</li>
</ul>
</li>
<li><p><strong>용도</strong> : 주로 개발 환경에서 사용된다. Flutter의 hot-reload 기능이 JIT 컴파일을 사용한 대표적인 예시이다.</p>
</li>
</ul>
<h4 id="aot-컴파일러-ahead-of-time">AOT 컴파일러 (Ahead-Of-Time)</h4>
<ul>
<li><p><strong>특징</strong> : Dart AOT 컴파일러는 프로그램 실행 전에 전체 코드를 미리 컴파일한다.</p>
</li>
<li><p><strong>장점</strong> </p>
<ul>
<li><strong>빠른 실행 시간</strong> : 프로그램이 미리 컴파일되어 있기 때문에, 실행 속도가 빠르다. 이는 모바일 앱과 같이 런타임 성능이 중요한 환경에서 매우 유리하다.</li>
<li><strong>작은 실행 파일</strong> : 불필요한 코드가 제거되고 최적화가 적용되어 실행 파일 크기가 작아질 수 있다.</li>
</ul>
</li>
<li><p><strong>용도</strong> : 주로 배포 환경에서 사용된다. Flutter의 릴리즈 모드에서의 빌드가 AOT 컴파일을 사용하는 좋은 예시이다.</p>
</li>
</ul>
<h4 id="요약">요약</h4>
<ul>
<li><strong>JIT 컴파일러</strong> : 개발 시 빠른 피드백과 동적 최적화를 제공하여 효율적인 개발을 지원한다.</li>
<li><strong>AOT 컴파일러</strong> : 배포 시 빠른 실행 속도와 작은 파일 크기를 제공하여 효율적인 애플리케이션 실행을 지원한다.</li>
</ul>
<hr>
<h3 id="장점">장점</h3>
<ol>
<li><p><strong>Flutter와의 통합</strong> <br>Dart는 Google의 UI프레임워크인 Flutter의 주언어로 사용되어, Flutter 앱 개발에 최적화되어 있다.<br/></p>
</li>
<li><p><strong>성능</strong> <br>Dart는 뛰어난 성능을 제공하며, 특히 Flutter 애플리케이션은 고성능UI를 제공하는 데 강점을 보인다.<br/></p>
</li>
<li><p><strong>Hot Reload</strong> <br>앱 개발 시 수정사항을 즉시 확인할 수 있는 Hot Reload 기능은 생산성을 향상시키는 데 큰 기여를 한다.<br/></p>
</li>
<li><p><strong>종합적인 개발환경</strong> <br>Dart는 다양한 개발 도구와 라이브러리를 제공하여 풍부한 개발 경험을 제공한다.<br/></p>
</li>
<li><p><strong>플랫폼 독립성</strong> <br>Flutter를 통해 Dart 코드를 사용하여 iOS, Android, 웹 등 다양한 플랫폼에서 동일한 코드베이스로 애플리케이션을 개발할 수 있다.<br/></p>
</li>
</ol>
<h3 id="단점">단점</h3>
<ol>
<li><p><strong>생태계의 제한</strong> <br>다른 언어에 비해 Dart 생태계는 아직 크기가 작다. 이로 인해 사용 가능한 라이브러리 도구의 다양성이 부족할 수 있다.<br/></p>
</li>
<li><p><strong>커뮤니티 부족</strong> <br>Dart 개발자 커뮤니티는 상대적으로 작아 여전히 확장 중이다. 이로 인해 지원 및 자료확보가 다른 언어에 비해 제한될 수 있다.<br/></p>
</li>
<li><p><strong>모바일 앱 외 다른 사용영역의 부족함</strong> <br>Flutter를 통한 모바일앱 개발에 초점을 맞추고 있어, 다른 사용 영역에서는 다른 언어가 더 적합할 수 있다.<br/></p>
</li>
</ol>
<hr>
<h1 id="구조">구조</h1>
<p>플러터(Flutter) 애플리케이션의 구조는 다음과 같이 크게 <code>위젯 트리(Widget Tree)</code>와 <code>렌더링 트리(Rendering Tree)</code>로 나뉜다. 이 두 가지 트리는 플러터의 UI 구성과 화면 렌더링을 관리하는 핵심 요소이다.</p>
<h2 id="위젯-트리-widget-tree">위젯 트리 (Widget Tree)</h2>
<p>플러터에서 UI는 위젯으로 구성된다. 위젯은 모든 것을 포함하는 <code>기본 구성 요소</code>이다. 텍스트, 버튼, 레이아웃, 애니메이션 등 모든 것이 위젯으로 구성된다. 위젯은 상태가 있는 StatefulWidget과 상태가 없는 StatelessWidget으로 나눌 수 있다.</p>
<blockquote>
<p>StatefulWidget : 상태가 변할 수 있는 위젯. 위젯의 상태가 변경되면, Flutter는 위젯을 다시 빌드하여 변경된 상태를 반영. 상호작용이네 다른 요인에 의해 상태가 변경될 수 있는 경우에 사용. 예) 폼 (Form) 입력, 애니메이션, 동적으로 변경되는 데이터 등</p>
<p>StatelessWidget : 상태가 변하지 않는 위젯. 빌드 후 변경 X. 한 번 렌더링된 후에는 변경되지 않는 UI를 만드는 데 사용. 예) 단순한 텍스트 레이블, 아이콘, 이미지 등
{: .prompt-tip }</p>
</blockquote>
<p><strong>주요 특징</strong></p>
<ul>
<li><p>위젯 : 모든 UI 요소는 위젯으로 표현된다. 예를 들어, Text, RaisedButton, Container 등은 모두 위젯이다.</p>
</li>
<li><p>구성 요소 : 위젯은 다른 위젯을 포함하여 복잡한 UI를 구성할 수 있다. 이는 트리 구조로 이루어져있다.</p>
</li>
<li><p>재사용성 : 위젯은 재사용이 가능하며, 플러터 애플리케이션의 다양한 부분에 쉽게 사용될 수 있다.</p>
</li>
</ul>
<hr>
<h2 id="렌더링-트리-rendering-tree">렌더링 트리 (Rendering Tree)</h2>
<p>렌더링 트리는 플러터의 위젯 트리에서 실제 화면에 픽셀을 그리기 위해 사용된다. 각 위젯은 해당 위젯에 대한 렌더링을 담당하는 RenderObject를 가지고 있다. RenderObject는 화면에 어떻게 그릴지에 대한 정보를 가지고 있다.</p>
<p><strong>주요 특징</strong></p>
<ul>
<li><p>렌더링 : 각 위젯의 RenderObject는 해당 위젯을 화면에 그리기 위한 정보를 포함하고 있다.</p>
</li>
<li><p>최적화 : 렌더링 트리는 플러터 엔진이 화면을 효율적으로 그리기 위해 사용하는 구조이다. 필요한 경우 Flutter 엔진은 플랫폼의 하드웨어 가속을 이용하여 렌더링 속도를 향상시킬 수 있다.</p>
</li>
<li><p>레이어 : 렌더링 트리는 실제 그래픽 처리를 위한 레이어들로 구성된다. 각 레이어는 화면의 일부분을 나타내며, 필요에 따라 효율적으로 관리된다.</p>
</li>
</ul>
<h2 id="요약-1">요약</h2>
<p>플러터 애플리케이션의 구조는 위젯 트리를 기반으로 하며, 각 위젯은 RenderObject를 통해 렌더링 트리에 연결된다. 위젯 트리는 UI의 구조와 동작을 정의하고, 렌더링 트리는 실제 화면에 어떻게 표시할지를 결정한다. 이 구조는 플러터의 UI 개발을 단순화하고, 화면 렌더링을 효율적으로 처리할 수 있도록 한다.</p>
<hr>
<h1 id="장단점">장단점</h1>
<h2 id="flutter의-장점">Flutter의 장점</h2>
<ul>
<li><p>하나의 코드로 iOS, Android, macOS, window, web 모두 동작 가능</p>
</li>
<li><p>React Native의 경우 머터리얼(Material) 디자인을 적용 시 시스템에 있는 그래픽 라이브러리로 동작하기 때문에 iOS, Android 위젯의 스타일이 달라 보일 수 있다. 반면에 Flutter는 SKIA 엔진을 탑재하여 그래픽 라이브러리가 OS 종속성을 갖고 있지 않아 iOS, Android 모두 같은 스타일로 보여준다.</p>
</li>
<li><p>핫 리로딩을 지원 (앱을 처음부터 다시 실행하지 않고 새로운 코드가 반영됨)</p>
</li>
<li><p>배우기 쉽다(dart)</p>
</li>
</ul>
<h2 id="flutter의-단점">Flutter의 단점</h2>
<ul>
<li><p>React Native 보다 성능적인 면에서는 빠르다고 할 수 있으나 생태계가 크지 않다.</p>
</li>
<li><p>아직까진 네이티브(Android, Swfit) 보다는 성능적인 면에서 앞서지 않음</p>
</li>
<li><p>현재 지속적으로 업데이트가 되어 있어 deprecated된 api가 정리되지 않아 개선 필요</p>
</li>
</ul>
<h2 id="vs-react-native">vs React Native</h2>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/d861f43d-6993-40cd-8040-9212854f8d5c/image.png" alt=""></p>
<p>종합적으로 판단했을 때 성능적인 우위로는 React Native 보다는 플러터가 우수 하나, 생태계가 크지 않다. 나온 지 얼마 되지 않은 상태라서 커뮤니티가 아직 부족한 상태라고 보면 된다. 초기 플러터의 경우 안드로이드의 JNI 와같은 C++ Native Method를 사용하지 못하였는데 1.1 버전부터는 Native Method를 사용할 수 있는 인터페이스가 추가 됐다. </p>
<p>미래 관점으로 구글의 차세데 OS 퓨시아의 공식 언어로 Dart로 정했으며 플러터로 개발한 앱은 바로 퓨시아 OS에서 구동이 가능하다. 차세대 OS의 공식 언어로 지정되었으므로 커뮤니티나 생태계는 확장될 가능성은 있어 보인다.</p>
<p>출처 - <a href="https://overface.tistory.com/746">https://overface.tistory.com/746</a></p>
<hr>
<p><strong>플러터가 React Native보다 빠른 이유</strong></p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/e1f0a282-6c31-4835-8206-09b1477660b5/image.png" alt=""></p>
<p> React Native는 UI 렌더링에 네이티브 위젯을 사용하지만, 연산은 JS 런타임에 의존한다. JS와 네이티브 위젯이 통신하기 위해 Bridge를 거칠 때 마다 큰 비용이 든다. 그와 반대로 플러터는 그 자체에 필요한 대부분의 구성 요소를 포함 한다. React Native처럼 Bridge가 필요 없어 Skia 엔진을 사용하여 네이티브와 근소한 렌더링 속도를 갖게 된다.</p>
<hr>
<h1 id="세줄-정리">세줄 정리</h1>
<ul>
<li><p>단일소스로 iOS 모바일, 안드로이드 애플리케이션을 개발할 수 있는 UI 프레임워크이다.</p>
</li>
<li><p>구글에서 만들고 Dart 언어로 개발한다.</p>
</li>
<li><p>공식 개발환경 IDE는 VSCode와 안드로이드 스튜디오이다. (인텔리제이도 가능)</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript - 정규표현식]]></title>
            <link>https://velog.io/@nawhes_joo/JavaScript-%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D</link>
            <guid>https://velog.io/@nawhes_joo/JavaScript-%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D</guid>
            <pubDate>Mon, 27 May 2024 08:19:12 GMT</pubDate>
            <description><![CDATA[<h1 id="정규표현식이란">정규표현식이란?</h1>
<blockquote>
<p>정규표현식(regular expression)은 문자열에서 특정한 문자를 찾아내는 도구다. 이 도구를 이용하면 수십줄이 필요한 작업을 한 줄로 끝낼 수 있다.</p>
</blockquote>
<hr>
<h1 id="정규표현식-생성">정규표현식 생성</h1>
<p>정규표현식은 두가지 단계로 이루어진다. 하나는 <code>컴파일(compile)</code>, 다른 하나는 <code>실행(execute)</code>이다.</p>
<p>우선 컴파일부터 알아보자.</p>
<h2 id="컴파일">컴파일</h2>
<p>컴파일은 검출하고자 하는 패턴을 만드는 일이다. 우선 정규표현식 객체를 만들어야 한다. 객체를 만드는 방법은 두 가지가 있다. a라는 텍스트를 찾아내는 정규식을 만들어보자.</p>
<h3 id="정규표현식-리터럴">정규표현식 리터럴</h3>
<pre><code class="language-javascript">var pattern = /a/;</code></pre>
<h3 id="정규표현식-객체-생성자">정규표현식 객체 생성자</h3>
<pre><code class="language-javascript">var pattern = new RegExp(&#39;a&#39;);</code></pre>
<p>두가지 모두 같은 결과를 만들지만 각자가 장단점이 있다.</p>
<hr>
<h2 id="정규표현식-메소드-실행">정규표현식 메소드 실행</h2>
<p>정규표현식을 컴파일해서 객체를 만들었다면 이제 문자열에서 원하는 문자를 찾아내야 한다.</p>
<h3 id="regexpexec">RegExp.exec()</h3>
<pre><code class="language-javascript">console.log(pattern.exec(&#39;abcdef&#39;)); // [&#39;a&#39;]</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/3ce0002f-6ffc-4de9-a822-cd4981885719/image.png" alt=""></p>
<p>실행결과는 문자열 a를 값으로 하는 배열을 리턴한다.</p>
<pre><code class="language-javascript">console.log(pattern.exec(&#39;bcdefg&#39;)); // null</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/490c1355-1620-4c18-b49f-5d8bc2b965e9/image.png" alt=""></p>
<p>인자 &#39;bcdefg&#39;에는 a가 없기 때문에 null을 리턴한다.</p>
<hr>
<h3 id="regexptest">RegExp.test()</h3>
<pre><code class="language-javascript">console.log(pattern.test(&#39;abcdef&#39;)); // true
console.log(pattern.test(&#39;bcdefg&#39;)); // false</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/586eb0ee-5847-4910-ab5e-6d34e2f2121a/image.png" alt=""></p>
<p>test는 인자 안에 패턴에 해당되는 문자열이 있으면 true, 없으면 false를 리턴한다.</p>
<hr>
<h2 id="문자열-메소드-실행">문자열 메소드 실행</h2>
<p>문자열 객체의 몇몇 메소드는 정규표현식을 사용할 수 있다.</p>
<h3 id="stringmatch">String.match()</h3>
<pre><code class="language-javascript">console.log(&#39;abcdef&#39;.match(pattern));
console.log(&#39;bcdefg&#39;.match(pattern));</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/143dcce9-da9e-4152-b231-6400d872076d/image.png" alt=""></p>
<p>RegExp.exec()와 비슷하다.</p>
<hr>
<h3 id="stringreplace">String.replace()</h3>
<pre><code class="language-javascript">console.log(&#39;abcdef&#39;.replace(pattern, &#39;A&#39;));</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/c19292b2-1bbe-40bc-9ec1-7762f1aff89f/image.png" alt=""></p>
<p>문자열에서 패턴을 검색하여 이를 변경한 후에 변경된 값을 리턴한다.</p>
<hr>
<h2 id="옵션">옵션</h2>
<p>정규표현식 패턴을 만들 때 옵션을 설정할 수 있다. 옵션에 따라서 검출되는 데이터가 달라진다.</p>
<h3 id="i">i</h3>
<pre><code class="language-javascript">var xi = /a/;
console.log(&#39;Abcde&#39;.match(xi)); // null

var oi = /a/i;
console.log(&#39;Abcde&#39;.match(oi)); // [&#39;A&#39;]</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/f871c3ed-de2b-403b-b402-04cb7e5caac3/image.png" alt=""></p>
<p>i를 붙이면 대소문자를 구분하지 않는다.</p>
<hr>
<h3 id="g">g</h3>
<pre><code class="language-javascript">var xg = /a/;
console.log(&#39;abcdea&#39;.match(xg)); // [&#39;a&#39;]

var og = /a/g;
console.log(&#39;abcdea&#39;.match(og)); // [&#39;a&#39;, &#39;a&#39;]</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/541f2fdd-fa91-4a97-98ae-523a234545f2/image.png" alt=""></p>
<p>g를 붙이면 검색된 모든 결과를 리턴한다.</p>
<hr>
<h2 id="캡처">캡처</h2>
<pre><code class="language-javascript">var pattern = /(\w+)\s(\w+)/;
var str = &quot;Hello world&quot;;
var result = str.replace(pattern, &quot;$2, $1&quot;);
console.log(result);</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/bd09312b-4a48-4c51-84f8-a95fda1d4847/image.png" alt=""></p>
<p>괄호안의 패턴은 마치 변수처럼 재사용할 수 있다. 이 때 기호 $를 사용하는데 위 예시는 Hello와 world의 순서를 역전시킨다.</p>
<pre><code class="language-javascript">var result1 = str.replace(pattern, &quot;$2 $1&quot;); // 콤마 제거
var result2 = str.replace(pattern, &quot;$1$2&quot;);     // 공백 제거
var result3 = str.replace(pattern, &quot;$1 $2 $2$1 $1+$2&quot;); // 짬뽕</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/4510f3fa-db98-4fc0-8e3c-7d3b56f9b7c8/image.png" alt=""></p>
<p>순서뿐만 아니라 $1과 $2 사이에 구분자, 공백 등을 추가할 수 있다.</p>
<hr>
<h2 id="치환">치환</h2>
<pre><code class="language-javascript">var urlPattern = /\b(?:https?):\/\/[a-z0-9-+&amp;@#\/%?=~_|!:,.;]*/gim;
var content = &#39;블로그 : https://nawhes.techblog.doubleuem.com/ 입니다. 네이버 : http://naver.com 입니다. &#39;;
var result = content.replace(urlPattern, function(url){
    return &#39;&lt;a href=&quot;&#39;+url+&#39;&quot;&gt;&#39;+url+&#39;&lt;/a&gt;&#39;;
});
console.log(result);</code></pre>
<pre><code class="language-javascript">블로그 : &lt;a href=&quot;https://nawhes.techblog.doubleuem.com/&quot;&gt;https://nawhes.techblog.doubleuem.com/&lt;/a&gt; 입니다. 네이버 : &lt;a href=&quot;http://naver.com&quot;&gt;http://naver.com&lt;/a&gt; 입니다. </code></pre>
<p>replace를 통해 URL을 링크 html 태그로 교체하였다.</p>
<h3 id="분석">분석</h3>
<ol>
<li><p><code>/.../</code> : 정규 표현식 리터럴
 정규표현식을 감싸는 슬래시는 자바스크립트에서 정규표현식 리터럴을 정의</p>
</li>
<li><p><code>\b</code> : 단어 경계 (word boundary)
<code>\b</code>는 단어 경계를 나타낸다. 단어 경계는 <code>\w</code> (단어 문자: 알파벳, 숫자, 밑줄)와 <code>\W</code> (비단어 문자: 공백, 특수 문자 등) 사이의 위치를 의미한다. 즉, URL이 단어의 시작 부분에 위치해야 함을 나타낸다.</p>
</li>
<li><p><code>(?...)</code> : 캡처하지 않는 그룹 (non-capturing group)
 <code>(?...)</code>는 캡처하지 않는 그룹을 나타낸다. 이는 그룹화를 위해 사용되지만, 해당 그룹의 매칭 결과를 메모리에 저장하지 않는다. 위 코드의 경우 그룹 안의 내용은 <code>https</code> 또는 <code>http</code>가 된다.</p>
</li>
<li><p><code>https?</code> : http 또는 https
 <code>s?</code>는 <code>s</code>가 0번 또는 1번 나타날 수 있음을 의미한다.</p>
</li>
<li><p><code>:\/\/</code> : 콜론과 슬래시 두 개
 <code>:\/\/</code>sms <code>://</code>를 문자 그대로 매칭한다. 슬래시(<code>/</code>)와 콜론(<code>:</code>)은 정규표현식에서 특수 문자가 아니므로 그대로 사용된다.</p>
</li>
<li><p><code>[a-z0-9-+&amp;@#\/%?=~_|!:,.;]*</code> : URL의 나머지 부분
 이 부분은 URL의 나머지 부분을 매칭한다. 대괄호(<code>[]</code>) 안의 내용은 문자 클래스를 나타내며, 이 클래스 안의 어떤 문자도 매칭될 수 있다. 아스테리스크(<code>*</code>)는 앞의 요소가 0번 이상 반복될 수 있음을 의미한다.</p>
<p> 문자 클래스 내의 문자들 : </p>
<ul>
<li><code>a-z</code> : 소문자 알파벳</li>
<li><code>0-9</code> : 숫자</li>
<li><code>-</code> : gkdlvms</li>
<li><code>+&amp;@#</code> : 각 문자들</li>
<li><code>\/</code> : 슬래시(여기서 슬래시는 이스케이프되어 문자 그대로의 슬래시를 의미)</li>
<li><code>%?=~_|!:,.;</code> : 각 문자들</li>
</ul>
</li>
<li><p><code>*</code> : 0번 이상 반복
 문자 클래스 뒤의 <code>*</code>는 해당 클래스에 정의된 문자가 0번 이상 반복될 수 있음을 의미</p>
</li>
<li><p><code>gim</code> : 플래그
 정규표현식의 끝에 붙는 플래그는 검색 방식을 조정한다</p>
<ul>
<li><code>g</code>(global) : 전체 문자열에서 모든 매칭을 찾는다</li>
<li><code>i</code>(ignore case) : 대소문자를 구분하지 않는다.</li>
<li><code>m</code>(multiline) 여러 줄에 걸쳐 매칭을 수행한다. 여기서는 큰 의미가 없지만, 줄바꿈 문자가 있을 때 각 줄을 개별 문자열로 처리한다.</li>
</ul>
</li>
</ol>
<h3 id="전체-요약">전체 요약</h3>
<ul>
<li><code>http</code> 또는 <code>https</code>로 시작</li>
<li><code>://</code>로 이어짐</li>
<li>URL의 나머지 부분은 소문자 알파벳, 숫자, 하이픈, 플러스 등 하나 이상의 문자로 이루어짐</li>
</ul>
<p>이 정규표현식은 대소문자를 구분하지 않고, 전체 문자열에서 모든 매칭을 찾으며, 여러 줄에서 작동한다.</p>
<hr>
<h1 id="메타문자와-이스케이프">메타문자와 이스케이프</h1>
<h2 id="메타문자">메타문자</h2>
<blockquote>
<p>메타문자는 정규표현식에서 특별한 의미를 가지는 문자들이다. 이 문자는 문자 그대로 매칭되지 않고, 특정 패턴이나 동작을 정의하는 데 사용된다.</p>
</blockquote>
<ul>
<li><p><code>.</code> : 임의의 한 문자 (줄바꿈을 제외한 모든 문자). </p>
<ul>
<li><code>a.b</code> : a와 b사이에 어떤 문자가 와도 매칭 (&#39;a1b&#39;, &#39;a-b&#39;)</li>
</ul>
</li>
<li><p><code>^</code> : 문자열의 시작</p>
<ul>
<li><code>^abc</code> : 문자열이 &#39;abc&#39;로 시작할 때 매칭</li>
</ul>
</li>
<li><p><code>$</code> : 문자열의 끝</p>
<ul>
<li><code>abc$</code> : 문자열이 &#39;abc&#39;로 끝날 때 매칭</li>
</ul>
</li>
<li><p><code>*</code> : 앞의 요소가 0번 이상 반복됨</p>
<ul>
<li><code>a*</code> : &#39;a&#39;가 0번 이상 반복될 때 매칭 (&#39;&#39;, &#39;a&#39;, &#39;aaa&#39;)</li>
</ul>
</li>
<li><p><code>+</code> : 앞의 요소가 1번 이상 반복됨</p>
<ul>
<li><code>a+</code> : &#39;a&#39;가 1번 이상 반복될 때 매칭 (&#39;a&#39;, &#39;aaa&#39;)</li>
</ul>
</li>
<li><p><code>?</code> : 앞의 요소가 0번 또는 1번 나타남</p>
<ul>
<li><code>a?</code> : &#39;a&#39;가 0번 또는 1번 나타날 때 매칭 (&#39;&#39;, &#39;a&#39;)</li>
</ul>
</li>
<li><p><code>{n,m}</code> : 앞의 요소가 n번 이상 m번 이하 반복됨</p>
<ul>
<li><code>a{2,4}</code> : &#39;a&#39;가 2번 이상, 4번 이하 반복될 때 매칭 (&#39;aa&#39;, &#39;aaa&#39;, &#39;aaaa&#39;)</li>
</ul>
</li>
<li><p><code>[]</code> : 문자 클래스. 대괄호 안의 문자 중 하나와 매칭</p>
<ul>
<li><code>[abc]</code> : &#39;a&#39;, &#39;b&#39;, &#39;c&#39; 중 하나와 매칭</li>
</ul>
</li>
<li><p><code>|</code> : 논리적 OR</p>
<ul>
<li><code>a|b</code> : &#39;a&#39; 또는 &#39;b&#39;와 매칭</li>
</ul>
</li>
<li><p><code>()</code> : 그룹화 및 캡처</p>
<ul>
<li><code>(abc)</code> : &#39;abc&#39;라는 문자열과 매칭, 캡처 그룹으로 사용.</li>
</ul>
</li>
<li><p><code>\</code> : 이스케이프 문자 (특수문자나 메타문자를 그대로 매칭할 때 사용)</p>
</li>
</ul>
<hr>
<h2 id="이스케이프">이스케이프</h2>
<blockquote>
<p>이스케이프는 메타문자나 특수 문자를 문자 그대로 매칭하기 위해 사용하는 방법이다.
백슬레시<code>(\)</code>는 이스케이프 문자로 사용된다. 메타문자를 이스케이프하면, 그것이 특별한 의미를 가지지 않고 <code>문자 그대로</code> 매칭된다.</p>
</blockquote>
<ul>
<li><p><code>\.</code> : 문자 그대로의 점(.)</p>
</li>
<li><p><code>\*</code> : 문자 그대로의 별표(*)</p>
</li>
<li><p><code>\?</code> : 문자 그대로의 물음표(?)</p>
</li>
<li><p><code>\\</code> : 문자 그대로의 백슬래시</p>
</li>
</ul>
<hr>
<p>추가로, 정규표현식 내에서 특수 문자나 이스케이프 시퀀스를 사용하여 특정 문자나 패턴을 나타낼 수 있다.</p>
<ul>
<li><p><code>\d</code> : 숫자 문자 (0-9)</p>
</li>
<li><p><code>\D</code> : 숫자가 아닌 문자</p>
</li>
<li><p><code>\w</code> : 단어 문자 (알파벳, 숫자, 밑줄)</p>
</li>
<li><p><code>\W</code> : 단어 문자가 아닌 문자</p>
</li>
<li><p><code>\s</code> : 공백 문자 (스페이스, 탭, 줄바꿈 등)</p>
</li>
<li><p><code>\S</code> : 공백 문자가 아닌 문자</p>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript - 커링(Currying)]]></title>
            <link>https://velog.io/@nawhes_joo/JavaScript-%EC%BB%A4%EB%A7%81Currying</link>
            <guid>https://velog.io/@nawhes_joo/JavaScript-%EC%BB%A4%EB%A7%81Currying</guid>
            <pubDate>Mon, 20 May 2024 05:24:05 GMT</pubDate>
            <description><![CDATA[<h1 id="커링이란">커링이란?</h1>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/ea31e609-3444-4a30-b01a-654f69c13a4f/image.png" alt=""></p>
<p>출처 - <a href="https://velog.io/@hustle-dev/Javascript-%EC%BB%A4%EB%A7%81%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90">hustle-dev 벨로그</a></p>
<blockquote>
<p>커링(Currying)은 함수의 <code>재사용성을 높이기 위해 함수 자체를 return하는 함수</code>이다.</p>
<p>쉽게 말하면, <code>&#39;함수를 반환하는 함수&#39;</code>이다.</p>
</blockquote>
<p>함수를 하나만 사용할 때는 필요한 모든 파라미터를 한 번에 넣어야 한다. 커링을 사용하면 함수를 분리할 수 있으므로 파라미터도 나눠 전달할 수 있다.</p>
<ul>
<li>커링과 같이 함수 자체를 인자로 받거나 반환하는 함수를 <code>고차 함수</code>라고 부르기도 한다.</li>
</ul>
<p>커링이 왜 사용될까??</p>
<ul>
<li><p>함수의 재활용을 위해서</p>
<ul>
<li>원하는 함수들을 조합해서 사용할 수 있다.</li>
</ul>
</li>
<li><p>하나 이상의 인수의 함수를, 하나의 인수를 받는 함수로 축소할 수 있다.</p>
<ul>
<li>그래서 더 가벼운 함수 제작이 가능하다.</li>
</ul>
</li>
</ul>
<hr>
<h1 id="커링">커링</h1>
<h2 id="일반적인-커링">일반적인 커링</h2>
<pre><code class="language-javascript">function add(a,b){
  return a+b;
}</code></pre>
<p>다음 add 함수를 재활용 하려면?</p>
<pre><code class="language-javascript">function addTwo(a){
  return add(a,2)
}</code></pre>
<p>이렇게 2+@를 해주는 addTwo 함수를 만들 수 있다.</p>
<p>만약 add3, add4, add5... 등의 여러 함수가 필요하다고 하자.</p>
<pre><code class="language-javascript">function addTwo(a){
  return add(a,2);
}
function addThree(a){
  return add(a,3);
}
function addFour(a){
  return add(a,4);
}</code></pre>
<p>분명 add 함수를 재활용하긴 하지만, 뭔가 이상하다.</p>
<p>여기에 커링을 적용해보자.</p>
<pre><code class="language-javascript">function add(a,b){
    return console.log(a+b);
}

function addX(x){
    return function(a){
      return add(a, x);
    }
  }

  const addTwo = addX(2);
  const addThree = addX(3);
  const addFour = addX(4);

  addTwo(2); // 2 + 2 = 4
  addThree(5); // 3 + 5 = 8
  addFour(1); // 4 + 1 = 5</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/e74fc8d1-8f55-463c-a0c8-8ac182374dec/image.png" alt=""></p>
<p>다음과 같이 addX 함수를 이용해서 여러 파생 함수들을 만들 수 있다.</p>
<p>addX 함수는 이렇게 동작한다.</p>
<ul>
<li><p>인자 x를 받아서 익명함수를 반환한다. 그 함수는 function(a){ return add(a,x); } 의 형태이다.</p>
</li>
<li><p>반환받은 값인 함수는 인자 a를 요구한다. a를 넣어주면 add(a,x)의 실행 결과를 반환한다.</p>
</li>
</ul>
<p>이처럼 함수 실행을 위한 인자를 한 번에 받지 않고, 여러 차례에 나눠서 받을 수 있다.</p>
<p>이 예시를 화살표 함수로 나타내면,</p>
<pre><code class="language-javascript">function addX(x){
  return function(a){
    return add(a, x);
  }
}

const addX = x =&gt; a =&gt; add(a,x);</code></pre>
<p>이렇게 더 간단해진다.</p>
<hr>
<h2 id="함수를-인자로-받는-커링함수">함수를 인자로 받는 커링함수</h2>
<p>자바스크립트에서는 함수도 값이다.</p>
<p>그러므로 함수의 인자로 또 다른 함수도 받을 수 있다.</p>
<pre><code class="language-javascript">function curry(fn){
  return function(a){
    return function(b){
      return fn(a,b);
    }
  }
}</code></pre>
<p>이제 이 함수를 어떻게 써야할까?</p>
<p>정확히 위에서 선언했던 addX와 동일하게 만들 수 있다.</p>
<pre><code class="language-javascript">function add(a,b){
  return a+b;
}

const addX = curry(add);
const addTwo = addX(2);
console.log(addTwo(5));</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/b6104e4a-88cf-4f09-9e6b-8354f14cb8b4/image.png" alt=""></p>
<p>함수 자체를 인자로 받아서 활용할 수 있는 것을 알게 되면, 코드를 작성하는 스타일이 더 다양해진다.</p>
<hr>
<h2 id="객체-데이터를-가져오는-커링">객체 데이터를 가져오는 커링</h2>
<p>커링을 사용하지 않은 경우 :</p>
<pre><code class="language-javascript">const todos = [
  { id: 3, content: &#39;HTML&#39;, completed: false },
  { id: 2, content: &#39;CSS&#39;, completed: true },
  { id: 1, content: &#39;Javascript&#39;, completed: false}
];

const getTodosIdArr = todos =&gt; todos.map(todo =&gt; todo.id);
const getTodosContentArr = todos =&gt; todos.map(todo =&gt; todo.content);
const getTodosCompletedArr = todos =&gt; todos.map(todo =&gt; todo.completed)

console.log(getTodosIdArr(todos));            // [ 3, 2, 1 ]
console.log(getTodosContentArr(todos));        // [ &#39;HTML&#39;, &#39;CSS&#39;, &#39;Javascript&#39; ]
console.log(getTodosCompletedArr(todos));    // [ false, true, false ]</code></pre>
<p>일반적인 경우 코드를 작성하면 위와 같이 작성하게 된다. 여기에 커링을 적용해보자.</p>
<p>커링을 사용한 경우 :</p>
<pre><code class="language-javascript">const todos = [
    { id: 3, content: &#39;HTML&#39;, completed: false },
    { id: 2, content: &#39;CSS&#39;, completed: true },
    { id: 1, content: &#39;Javascript&#39;, completed: false }
];

const get = property =&gt; object =&gt; object[property];

const getId = get(&#39;id&#39;); // object =&gt; object[&#39;id&#39;];
const getContent = get(&#39;content&#39;); // object =&gt; object[&#39;content&#39;];
const getCompleted = get(&#39;completed&#39;); // object =&gt; object[&#39;completed&#39;]

const getTodosIdArr = todos =&gt; todos.map(getId);
const getTodosContentArr = todos =&gt; todos.map(getContent);
const getTodosCompletedArr = todos =&gt; todos.map(getCompleted);

console.log(getTodosIdArr(todos)); // [ 3, 2, 1 ]
console.log(getTodosContentArr(todos)); // [ &#39;HTML&#39;, &#39;CSS&#39;, &#39;Javascript&#39; ]
console.log(getTodosCompletedArr(todos)); // [ false, true, false ]</code></pre>
<blockquote>
<p>커링을 사용하는 경우 인자의 순서가 중요하다.
앞에 존재하는 인자일 수록 변동 가능성이 적고, 뒤에 있는 인자일 수록 변동 가능성이 높기 때문에 이 순서를 고려하여 설계하는 것이 중요하다고 한다.</p>
</blockquote>
<hr>
<h1 id="커링-응용">커링 응용</h1>
<p>function 키워드 없이 화살표 함수로 표현하여 방정식 4x^(x+2)를 수행할 함수를 만들어보자.</p>
<pre><code class="language-javascript">const add = (a,b) =&gt; a + b;
const multiply = (a,b) =&gt; a * b;

const addX = x =&gt; a =&gt; add(a, x);
const addTwo = addX(2);

const multiplyX = x =&gt; a =&gt; multiply(a, x);
const multiplyFour = multiplyX(4);</code></pre>
<p>이렇게 커링을 이용해 addTwo, multiplyFour 함수를 만들었다.</p>
<pre><code class="language-javascript">const compose = fn =&gt; fn2 =&gt; x =&gt; fn2(x) * fn(x);
const equation = compose(addTwo)(multiplyFour);
equation(10) // (4 * 10) * (10 + 2) = 40 * 12 = 480</code></pre>
<p>equation 함수에 넣는 값이 x가 되어 4x(x+2)의 다양한 값에 대한 결과를 받을 수 있다.</p>
<p>만약 5x(x+2)의 결과를 반환하는 함수가 필요하다면?</p>
<pre><code class="language-javascript">const multiplyFive = multiplyX(5);
const equaion2 = compose(addTwo)(multiplyFive);
equation2(10) // (5 * 10) * (10 + 2) = 600</code></pre>
<p>이렇게 표현할 수 있다.</p>
<p>마지막으로 위에서 선언한 equation 함수를 생성하면서 중복되는 부분을 제거하면,</p>
<pre><code class="language-javascript">const addTwo = addX(2);
const addFour = addX(4);
const multiplyFour = multiplyX(4);
const multiplyFive = multiplyX(5);

const compose = fn =&gt; fn2 =&gt; x =&gt; fn2(x) * fn(x);
const composeAddTwo = compose(addTwo);

const equation1 = composeAddTwo(multiplyFour); // 4x(x+2)
const equation2 = composeAddTwo(multiplyFive); // 5x(x+2)
const equation3 = composeAddTwo(addFour) // (x+4)(x+2)</code></pre>
<p>위와 같이 인자로 받은 함수들을 교체해주면서, 유사한 함수들을 여러개 만들 수 있다.</p>
<p>참고 - <a href="https://gobae.tistory.com/139">Go devlog</a>, <a href="https://velog.io/@hustle-dev/Javascript-%EC%BB%A4%EB%A7%81%EC%97%90-%EB%8C%80%ED%95%B4-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90#%EC%BB%A4%EB%A7%81%EC%9D%98-%ED%99%9C%EC%9A%A9">hustle-dev velog</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript - 스코프 체인(scope chain)]]></title>
            <link>https://velog.io/@nawhes_joo/JavaScript-%EC%8A%A4%EC%BD%94%ED%94%84-%EC%B2%B4%EC%9D%B8scope-chain</link>
            <guid>https://velog.io/@nawhes_joo/JavaScript-%EC%8A%A4%EC%BD%94%ED%94%84-%EC%B2%B4%EC%9D%B8scope-chain</guid>
            <pubDate>Wed, 08 May 2024 04:44:31 GMT</pubDate>
            <description><![CDATA[<h1 id="스코프-체인scope-chain이란">스코프 체인(scope chain)이란?</h1>
<p>스코프 체인(scope chain)은 일종의 리스트로서 전역 객체와 중첩된 함수의 스코프의 레퍼런스를 차례로 저장하고, 의미 그대로 각각의 <code>스코프가 어떻게 연결(chain)되고 있는지 보여주는 것</code>을 말한다.</p>
<p>하지만 스코프 체인(scope chain)을 이해하기 위해서 먼저 자바스크립트의 <code>실행 컨텍스트(Execution context)</code>를 알아야 한다.</p>
<hr>
<h1 id="실행-컨텍스트란-무엇인가">실행 컨텍스트란 무엇인가?</h1>
<p>실행 컨텍스트(Execution context)는 <code>우리가 작성한 코드가 실행되는 환경</code>을 말하며, scope, hoising, this, function, closure 등의 동작원리를 담고 있는 자바스크립트의 핵심원리를 말한다.</p>
<p>그리고 이 실행 컨텍스트에는 두 개의 실행 컨텍스트가 존재한다.</p>
<hr>
<h2 id="글로벌-실행-컨텍스트">글로벌 실행 컨텍스트</h2>
<p>코드가 실행되기 전에 생성이 되며, 함수 내에 없는 코드는 모두 <code>전역 실행 컨텍스트 안에 존재</code>한다.</p>
<p>그렇기 때문에, 자바스크립트 엔진은 일부 자바스크립트 코드를 실행할 때마다 글로벌 실행 컨텍스트(Global Execution Context)를 작성한다.</p>
<p>글로벌 실행 컨텍스트의 특징으로는 무조건 <code>하나의 전역 실행 컨텍스트만이 존재</code>하며, 어플리케이션이 종료될 때(웹 페이지에서 나가거나 브라우저를 닫을 때)까지 유지하는 것이다.</p>
<hr>
<h2 id="함수-실행-컨텍스트">함수 실행 컨텍스트</h2>
<p>전역 실행 컨텍스트가 생성된 후, 함수가 실행(ex 호출) 될 때마다 <code>새로운 실행 컨텍스트가 작성</code>된다.</p>
<hr>
<h1 id="실행-컨텍스트에서-스코프-체인scope-chain은-어떻게-작동하는가">실행 컨텍스트에서 스코프 체인(scope chain)은 어떻게 작동하는가?</h1>
<p>실행 컨텍스트는 <code>LIFO(Last in, First out) 구조의 스택</code>으로, 코드 실행 중에 생성된 <code>모든 실행 컨텍스트를 저장</code>하는 데 사용된다.</p>
<p>실행 컨텍스트가 실행되면, 엔진이 <code>스코프 체인</code>을 통해 <code>렉시컬 스코프를 먼저 파악</code>한다.</p>
<p>그러고 나서, 함수가 중첩 상태일 때 하위 함수 내에서 상위 함수의 스코프와 전역 스코프까지 참조할 수 있는데 이것을 <code>스코프 체인을 통해 탐색</code>하는 것이다.</p>
<pre><code class="language-javascript">var v = &#39;전역 변수&#39;;

function a() {
  // function a Execution Context(EC)
  var v = &#39;지역 변수&#39;;

  function b(){
    // function b Execution Context
    console.log(v);
  }

  b();
}

// Global Execution Context(GEC)
a();</code></pre>
<p>위 코드의 예제를 보면 먼저 글로벌 실행 컨텍스트(GEC)가 실행되고 스택에 쌓인다.</p>
<p>그런 다음 함수 호출 순으로 실행 컨텍스트 스택에 쌓이게 되고, 가장 나중에 호출된 b() 함수가 실행 컨텍스트 안에서부터 탐색을 시작한다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/51ecc995-90d2-44ed-ae7d-206f35f44aab/image.png" alt=""></p>
<p>출처 : <a href="https://medium.com/@pvivek4/scope-and-execution-context-in-javascript-3b71e76cd193">https://medium.com/@pvivek4/scope-and-execution-context-in-javascript-3b71e76cd193</a></p>
<p>그러면, b() 함수 안에서 변수 v를 탐색하기 시작하는데, 만약 변수 v가 없으면 b()함수를 감싸고 있는 외부 함수 a() 함수를 탐색하기 시작한다.</p>
<p>이때 a()함수 안에 변수 v가 존재하면 안에 있는 v를 참조하게 되고, 만약 없다면 마지막으로 전역 객체를 탐색하여 v를 찾아낸다.</p>
<p>결국 찾지 못한다면, v가 없다고 Uncaught ReferenceError : v is not defined 라는 에러가 발생한다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/149756de-f3cc-4a8c-ac24-d877dda1e832/image.png" alt=""></p>
<p>반대로 찾았다면, 결과값은 a() 안에 변수 v가 존재하기 때문에 <code>지역 변수</code>라는 값이 출력된다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/f2244875-153f-4946-95ea-97d46d83d000/image.png" alt=""></p>
<p>하지만 만약, a() 함수 안에 v를 제거한다면 전역 객체에 있는 변수 v의 값 <code>전역 변수</code>가 출력된다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/38cd6136-9713-44c2-8990-d97d9b859fc1/image.png" alt=""></p>
<p>이러한 과정들이 스코프에 담긴 순서대로 탐색하는 <code>스코프 체인</code>이라고 보면 된다.</p>
<hr>
<h1 id="스코프-체인을-개발자-도구로-확인하기">스코프 체인을 개발자 도구로 확인하기</h1>
<p>스코프 체인(scope chain)은 함수의 감춰진 프로퍼티인 [[Scope]]로 참조할 수 있다.</p>
<p><code>console.dir()</code>을 사용하면, 개발자 도구로 쉽게 확인이 가능하다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/9ae161fc-4741-4f15-a5d5-044515861914/image.png" alt=""></p>
<p>b() 함수에 <code>[[Scopes]] 속성이 존재</code>한다. 이것이 바로 스코프 체인(scope chain)이다.</p>
<p>자기 자신의 스코프(scope)를 제외한 자신과 가장 가까운 변수 객체의 스코프 순으로 접근하는 것을 눈으로 확인할 수 있다.</p>
<blockquote>
<p>정리하자면, 자기 자신의 스코프(scope)를 제외한 자신과 가장 가까운 변수 객체의 모든 스코프들을 스코프 체인이라 할 수 있다.</p>
</blockquote>
<p>출처 : <a href="https://ljtaek2.tistory.com/140">https://ljtaek2.tistory.com/140</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript - 렉시컬 스코프와 클로저]]></title>
            <link>https://velog.io/@nawhes_joo/JavaScript-%EB%A0%89%EC%8B%9C%EC%BB%AC-%EC%8A%A4%EC%BD%94%ED%94%84%EC%99%80-%ED%81%B4%EB%A1%9C%EC%A0%80</link>
            <guid>https://velog.io/@nawhes_joo/JavaScript-%EB%A0%89%EC%8B%9C%EC%BB%AC-%EC%8A%A4%EC%BD%94%ED%94%84%EC%99%80-%ED%81%B4%EB%A1%9C%EC%A0%80</guid>
            <pubDate>Tue, 07 May 2024 08:26:51 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><code>클로저</code>는 함수와 함수가 선언된 어휘적 환경의 조합이다. 클로저를 이해하려면 자바스크립트가 어떻게 변수의 유효범위를 지정하는지 <code>렉시컬 스코프(Lexical Scope)를 먼저 이해</code>해야 한다.</p>
</blockquote>
<h1 id="정적-스코프static-scope-렉시컬-스코프lexical-scope란">정적 스코프(static scope), 렉시컬 스코프(Lexical Scope)란?</h1>
<p><code>렉시컬 스코프</code>는 한 마디로 <code>함수를 어디에 선언하였는지에 따라 상위 스코프가 결정</code>되는 것을 말한다.</p>
<p><code>자바스크립트를 포함한 대부분의 프로그래밍 언어는 렉시컬 스코프를 따르며</code>, 이를 정적 스코프(Static Scope)라고 부르기도 한다.</p>
<pre><code class="language-javascript">function init(){
  var name = &quot;이름&quot;
  function displayName(){
    console.log(name);
  }
  displayName();
}
init();</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/f0792525-cf29-43d3-90da-c9024e044df6/image.png" alt=""></p>
<p>MDN에 나와있는 예제이다.</p>
<p>displayName()은 init() 안에 정의된 내부 함수이며 init() 함수 본문에서만 사용할 수 있다.</p>
<p>다만, 여기서 주의할 점은 displayName() 내부엔 자신만의 지역 변수가 없다는 점이다. 그런데 함수 내부에서 외부 함수의 변수에 접근할 수 있기 때문에 displayName() 역시 부모 함수 init()에서 선언된 변수 name에 접근할 수 있다.</p>
<p>상위스코프가 무엇인지 알려면 displayName 함수가 어디에 선언되었는지를 봐야하는데, 위 코드에서는 displayName 함수가 init 함수 안에서 선언되었으므로 <code>상위 스코프는 지역 스코프</code>가 된다.</p>
<p>그래서 init 함수 내의 선언된 name이라는 변수를 참조할 수 있는 것이다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/456eeb1b-00be-426d-8749-d8e823a3be32/image.png" alt=""></p>
<p>init()에 선언된 name이라는 변수가 존재하지 않고 상위스코프(전역)에 선언되었다면 전역에 선언된 name을 참조할 수 있다.</p>
<hr>
<h1 id="스코프scope란">스코프(Scope)란?</h1>
<p><code>스코프</code>는 <code>변수나 함수가 유효할 수 있는 범위</code>를 뜻하는데,
그렇기 때문에 자바스크립트 엔진이 <code>식별자를 검색할 때 사용하는 규칙</code>으로 작용한다.</p>
<hr>
<h1 id="클로저closure">클로저(Closure)</h1>
<p>사전적인 의미로는 <code>폐쇄</code>라는 뜻을 갖고 있다.
자바스크립트의 클로저도 이 폐쇄와 유사한 의미를 가지고 있다.</p>
<p>한 마디로 <code>클로저</code>란 함수가 선언될(생성될) 때 그 당시에 <code>주변의 환경과 함께 갇히는 것</code>을 말한다.</p>
<p>또 다른 말로, 함수가 속한 <code>렉시컬 스코프를 기억</code>하며, <code>함수가 렉시컬 스코프 밖에서 실행될 때도 이 스코프에 접근할 수 있게 해주는 기능</code>이다.</p>
<pre><code class="language-javascript">function sayHello(){
  const a = &#39;Hello&#39;;
  const b = &#39;World&#39;;

  function sumString(){
    console.log(a + &#39;&#39; + b);
  }

  return sumString;
}

const myFunc = sayHello();

myFunc();    // &#39;Hello World&#39;</code></pre>
<p>위 예제를 살펴보면, myFunc라는 변수는 sayHello 함수를 호출하고 있다.</p>
<p>그래서 myFunc를 실행하게 되면 문제 없이 &#39;Hello World&#39;가 잘 출력된다. 여기서 살펴볼 점은 myFunc의 부분은 변수 a와 b가 담겨있는 sayHello 함수 스코프의 바깥에 있음에도 불구하고 a와 b를 합친 Hello World를 잘 출력한다는 것인데, 그 이유가 바로 클로저(Closure) 때문이다.</p>
<p>모든 자바스크립트 함수는 선언될(생성될) 당시에 클로저가 형성되어 주변 환경, 즉 <code>렉시컬 스코프를 기억</code>할 수 있다.</p>
<hr>
<h2 id="클로저의-활용">클로저의 활용</h2>
<p>다음과 같은 상황에서 클로저가 유용하게 사용될 수 있다.</p>
<ul>
<li>정보 은닉 (접근 권한 제어)</li>
</ul>
<p>클로저 기법을 사용하는 이유는 <code>전역변수를 사용하지 않으려는 목적</code>이 강한 듯 하다.
누구나 접근 가능하고, 변경까지 할 수 있는 전역변수를 사용하는 것은 매우 위험하기 때문이다.
하지만 <code>클로저</code>를 사용하면 <code>전역변수를 사용하지 않으면서도 클로저 함수 내부의 변수에 계속 접근</code>할 수 있기 때문에 이 문제가 해결된다.</p>
<ul>
<li>커링(Currying)</li>
</ul>
<p><code>커링</code>은 f(a,b,c)처럼 <code>여러 개의 인자를 한 번의 호출로 처리하던 함수를 f(a)(b)(c) 처럼 분리하여 인자를 하나씩만 받는 여러개의 함수로 만드는 것</code>이다.</p>
<p>즉, 함수 하나가 n개의 인자를 받는 대신 n개의 함수를 만들어서 각각의 함수가 인자를 받도록 하는 것이다.
예제코드를 보면 커링을 구현할 때도 클로저가 들어간다.</p>
<pre><code class="language-javascript">// 일반 함수
function fn(x,y){
  return x + y;
}
console.log(fn(1,2));    // 3

// 일반 함수를 커링 함수로 변환
function curried_fn(x){
  return function(y){
    return x + y
  }
}

const curried_fn2 = x =&gt; y =&gt; x + y;

console.log(
  curried_fn(1)(2),    // 3
  curried_fn2(1)(2)    // 3
);</code></pre>
<p>※ 커링을 사용하는 이유</p>
<p>위 예제에서 일반함수의 경우 2개의 인자를 모두 전달받아야 해당 함수가 실행되는데 반해, 커링함수의 경우 첫번째 인자를 통해 하나의 함수를 실행하고, 이어 두번째 인자를 통해 또 하나의 함수를 독립적으로 실행한다. 따라서 인자를 담고있는 함수의 재사용이 가능해지고, 어떤 함수를 특정 애플리케이션 지점에서 다른 지점으로 값을 전달해야 하는 경우에도 유용하게 사용할 수 있다.</p>
<ul>
<li>부분적용함수</li>
</ul>
<p>위 커링함수를 조금 다른 방식으로 사용해볼 수도 있다.
<code>부분적용함수</code>란 n개의 인자를 받는 함수에 <code>미리 m개의 인자만 넘겨 기억</code>시켰다가, <code>나중에 (n-m)개의 인자</code>를 넘기면 비로소 원래 함수의 실행결과를 얻을 수 있도록 하는 함수를 말한다.
다시 말해, 하나의 고정값을 가지고 다른 인자들의 값을 변환시켜야 하는 경우에 이 부분적용함수를 사용할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript - 깊은 복사 / 얕은 복사]]></title>
            <link>https://velog.io/@nawhes_joo/JavaScript-%EA%B9%8A%EC%9D%80-%EB%B3%B5%EC%82%AC-%EC%96%95%EC%9D%80-%EB%B3%B5%EC%82%AC</link>
            <guid>https://velog.io/@nawhes_joo/JavaScript-%EA%B9%8A%EC%9D%80-%EB%B3%B5%EC%82%AC-%EC%96%95%EC%9D%80-%EB%B3%B5%EC%82%AC</guid>
            <pubDate>Fri, 03 May 2024 05:59:00 GMT</pubDate>
            <description><![CDATA[<ul>
<li>얕은 복사란, 참조형 타입의 값이 바로 아래 단계의 값만 복사하는 방법이다.</li>
<li>깊은 복사란, 참조형 타입 안의 모든 참조가 끊어지는 방법이다.</li>
</ul>
<h1 id="데이터-타입">데이터 타입</h1>
<p>JavaScript의 얕은 복사와 깊은 복사를 알아보기 전에 데이터 타입에 대해 알아보자.</p>
<ul>
<li>기본형(Primitive) 타입</li>
<li>참조형(Reference) 타입</li>
</ul>
<p>기본형 타입에는 Number, String, Boolean, undefined, null 그리고 ES6에서 추가된 Symbol이 있다.</p>
<p>참조형 타입은 Object, Array, Function, Date, RegExp, 그리고 ES6에서 추가된 Map, WeakMap, Set, WeakSet이 있다.</p>
<p>기본형과 참조형을 구분할 수 있는 방법은, 할당이나 연산 시 값을 복사하면 기본형이고, 값을 복사할 때 참조하면 참조형이다. 그럼 할당을 해보자.</p>
<pre><code class="language-javascript">var a = 10;</code></pre>
<p>코드에서는 변수를 선언하고, 식별자를 a로 주었으며 number 타입인 10을 할당했다. </p>
<p>이 코드를 실행했을 때, 메모리의 한 공간을 확보한 다음, 식별자를 붙인다. 그리고 바로 10을 할당하지 않고 데이터를 저장하기 위한 다른 메모리 공간을 하나 더 확보하고 그곳의 주소를 식별자가 저장된 곳의 값으로 집어넣는다. 그리고 10이라는 값을 집어넣는다.</p>
<p>즉 변수는 값의 <code>위치(주소)</code>를 기억하는 메모리 공간인데, 값의 위치란 값이 위치하고 있는 메모리 상의 <code>주소(address)</code>를 의미한다.</p>
<blockquote>
<p><code>변수</code>란 값이 위치하고 있는 메모리 주소에 접근하기 위해 사람이 이해할 수 있는 언어로 지정한 <code>식별자</code>다.</p>
</blockquote>
<p>값을 가져오는 과정은 위 과정과 반대다. <code>console.log</code>로 a에 접근했을 때 먼저 메모리 공간에서 식별자가 a인 메모리 공간을 찾고, 메모리 공간에 저장된 실제 값이 들어있는 공간의 주소로 값을 가져오는 것이다.</p>
<p>number 타입은 기본형 타입으로 주소가 한번 연결되었다. 그러면 참조형 타입을 알아보자.</p>
<pre><code class="language-javascript">var obj = {
  a : 10,
  b : &#39;abc&#39;,
};</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/99e2e0a0-6733-436d-8755-7bbd92350538/image.png" alt=""></p>
<ol>
<li><p>obj라는 식별자를 가진 변수를 선언하고 참조형 타입인 객체 하나를 할당했다. 기본형과 동일하게 메모리 공간(@1002)을 하나 확보하고 obj라는 식별자를 주었다. 그리고 값을 저장하려고 다른 메모리 공간(@5001)을 하나 더 확보했더니 기본형으로 저장해야 하는 값이 여러 개가 들어있다.</p>
</li>
<li><p>이 프로퍼티들을 저장하기 위해 방금 확보한 공간 외 프로퍼티 갯수에 맞게 또 여러 개의 공간(@7103, @7104)을 확보하고 식별자를 지정한다.</p>
</li>
<li><p>그리고 그 식별자의 실제 데이터가 저장할 공간을 다시 확보하고 (@5003, @5004) 실제 값을 저장한다.</p>
</li>
<li><p>@7103, @7104에 방금 저장한 곳의 주소를 저장 후, @5001에 그룹의 주소를 저장한다.</p>
</li>
<li><p>마지막으로, 아까 확보해둔 메모리 공간(@5001)에 값이 저장된 (@7103, @7104)의 공간 주소를 저장한다.</p>
</li>
</ol>
<p>기본형 데이터와의 차이는 참조형 데이터 안에 있는 기본형 데이터를 저장하기 위해 기본형 데이터의 주소를 담은 공간을 새로 생성했다는 점이다.</p>
<hr>
<h1 id="불변-값이란">불변 값이란?</h1>
<p>불변 값은 <code>변하지 않는 값</code>이라는 뜻이다. 위에서 살펴본 기본형 타입은 <code>불변 값</code>이다. 그리고 참조형 타입은 대체적으로 <code>가변 값</code>이다.</p>
<pre><code class="language-javascript">var a = 10;
a = 20;
console.log(a); // 20</code></pre>
<p>처음에는 a에 10 값을 주었다. 그리고 20 값을 다시 할당하고 출력하니 20이 나왔다. 자바스크립트 입장에서 설명해보자.</p>
<blockquote>
</blockquote>
<ul>
<li>메모리 공간을 확보 후 다른 메모리 공간에 10을 할당했다.</li>
<li>그리고 10이 저장된 주소를 a에 넣는다.</li>
<li>이후, 20을 재할당할 때, 10이 저장되어 있는 메모리 공간을 그대로 두고, 20을 저장하는 메모리 공간을 추가로 확보 후 저장한다.</li>
<li>그리고 그 공간의 주소를 a가 저장한다.</li>
</ul>
<p>흔히 사람의 입장에서는 10이 저장되어 있는 메모리 공간에 10을 삭제하고 20을 집어넣는 것을 생각한다. 하지만 자바스크립트는 값을 새로 생성하고 주소를 수정해주었다. 즉, 기본형 타입은 새로 생성되었고 변경되지 않았다. (10이라는 값은 추후 <code>GC(Garbage Collection)</code>가 수집하여 없어질 것임)</p>
<p>하지만 레퍼런스 타입은 다르다.</p>
<pre><code class="language-javascript">var obj = {
  a: 10,
  b: &#39;abc&#39;,
}
obj.a = 20;

console.log(obj.a) // 20</code></pre>
<p>obj의 프로퍼티의 값을 재할당하면, obj가 가지고 있는 메모리 주소는 변경되지 않고 obj.a가 가지고 있는 주소가 변경된다. 즉, &#39;새로운 객체&#39;가 만들어진 것이 아니로 기존 객체 내부의 값만 바뀐 것이다.</p>
<hr>
<h1 id="깊은-복사란">깊은 복사란?</h1>
<p>여기서 말하는 <code>복사</code>란, 원본을 베낌. 종이를 포개고 그 사이사이에 복사지를 받쳐 한 번에 여러 장을 쓴다는 의미를 가지고 있다. 즉 동일한 내용을 그대로 다른 곳에서 사용하는 것을 말한다.</p>
<p>그리고 깊은 복사란, 기존 값의 모든 참조가 끊어지는 것을 말한다. 특히 복사할 때, <code>참조형 타입 값(객체)</code>에서 내부에 있는 모든 값이 새로운 값이 되는 것을 말한다.</p>
<h2 id="기본형-타입의-깊은-복사">기본형 타입의 깊은 복사</h2>
<p>자바스크립트에서는 할당 연산자(<code>=</code>)를 사용해 쉽게 복사를 할 수 있다.</p>
<pre><code class="language-javascript">var a = 10;
var b = a;
console.log(a); // 10
console.log(b); // 10</code></pre>
<p>b에 10을 직접 할당해주지 않아도 a를 할당했기 때문에 10이라는 값이 도출된다. 하지만, 정확하게 말하자면 할당 연산자(<code>=</code>)는 객체를 복사해서 값에 집어넣는 것이 아니다. 단지 a가 가지고있는 <code>주소</code>를 b에게 주어 b도 그 <code>대상을 바라보게</code> 만든 것이다. 기본형 타입은 <code>불변 값</code>이라고 했다. 기본형 타입의 값을 재할당 할 때는 기존 값을 <code>변경</code>하는 것이 아닌 <code>새로 만들어 주소 값을 준다</code>는 의미였다. 따라서, a와 b는 메모리 공간에 생성된 10의 <code>주소값</code>을 가지고 있다.</p>
<pre><code class="language-javascript">var a = 10;
var b = 10;
console.log(a === b); // true

var c = 20;
var d = c;
console.log(c === d); // true

d = 30;
console.log(c); // 20
console.log(d); // 30
console.log(c === d); // false</code></pre>
<ul>
<li><p>기본형 타입의 값을 바라보는 <code>주소값</code>이 동일하기 때문에 a와 b는 각각 값을 할당 받았지만 동일하다고 판단한다.</p>
</li>
<li><p>d도 마찬가지로 c의 주소를 넘겨받았기 때문에 동일하다고 판단한다.</p>
</li>
<li><p>c의 주소를 넘겨받은 d에 30을 재할당했다.</p>
</li>
<li><p>c와 d는 다른 값을 바라보고 있다.</p>
</li>
</ul>
<p>내부의 값은 없지만 복사했을 때, 서로의 주소가 달라졌다. 기본형 타입의 <code>깊은 복사</code>다. <code>서로에게 영향을 주지 않는다.</code> </p>
<hr>
<h2 id="참조형-타입의-깊은-복사">참조형 타입의 깊은 복사</h2>
<pre><code class="language-javascript">var obj1 = {
  a: 10,
  b: &#39;abc&#39;,
};
var obj2 = obj1;
console.log(obj1 === obj2); // true

obj2.a = 20;
console.log(obj1);    // {a: 20, b: &#39;abc&#39;}
console.log(obj2);    // {a: 20, b: &#39;abc&#39;}
console.log(obj1 === obj2);    // true</code></pre>
<ul>
<li><p>obj1과 obj2가 가지고 있는 <code>주소 값</code>이 동일하기 때문에 true가 나왔다.</p>
</li>
<li><p>obj2의 a프로퍼티 값을 20으로 재할당했다.</p>
</li>
<li><p>obj1, obj2의 값이 a 프로퍼티 모두 20으로 변경되었다.</p>
</li>
<li><p>여전히 obj1과 obj2가 가지고 있는 주소 값이 동일하다.</p>
</li>
</ul>
<p>위 결과가 나온 이유는, obj2의 <code>프로퍼티</code>를 변경시켰기 때문이다. 프로퍼티 a가 바라보고 있는 주소 자체는 변경되었지만 obj1, obj2가 <code>프로퍼티 그룹</code>을 바라보는 주소 자체는 변경되지 않은 것이다.</p>
<blockquote>
<p><code>객체 자체의 참조 값</code>을 할당하면, <code>깊은 복사가 일어나지 않는다.</code></p>
</blockquote>
<p>그렇다면 객체의 깊은 복사를 하려면, 즉 내부의 프로퍼티 값의 주소를 전부 다르게 하려면 어떻게 해야할까?</p>
<h3 id="재귀-함수를-이용">재귀 함수를 이용</h3>
<pre><code class="language-javascript">var deepCopy = function (obj) {
  var result = {};
  if (typeof obj === &#39;object&#39; &amp;&amp; obj != null) {
    for(var prop in obj) {
      result[obj] = deepCopy(obj[prop]);
    }
  } else {
    result = obj;
  }
  return result;
}</code></pre>
<p>deepCopy 함수는 함수 내부에서 자기 자신을 호출하는 재귀 함수다. 중첩된 객체라고 하더라도 프로퍼티 갯수만큼 돌면서 result 객체에 새롭게 할당해준다.</p>
<hr>
<h3 id="jsonparsejsonstringifyobj를-사용">JSON.parse(JSON.stringify(obj))를 사용</h3>
<pre><code class="language-javascript">var obj1 = {
  a: 10,
  b: &#39;abc&#39;,
};
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.b = 3;
console.log(obj1);    // {a: 10, b: &#39;abc&#39;}
console.log(obj2);    // {a: 10, b: 3}</code></pre>
<p>JSON.stringify()는 JavaScript 값이나 객체를 JSON 문자열로 변환한다. 그리고 JSON.parse()는 JSON 문자열의 구분을 분석하고, 그 결과에서 JavaScript 값이나 객체를 생성한다. 즉, 객체를 문자열로 변환 후 다시 객체 형태로 만든다. 문자열로 변환 후 다시 객체로 만들면 원본 객체와의 참조가 모두 끊어진다.</p>
<hr>
<p>깊은 복사를 사용하면 복사했을 때 값은 동일하지만, 객체 내부의 값을 변경해도 <code>서로 영향을 주지 않고</code> 격리된 값을 보장한다.
원시값과 달리 객체는 변경이 가능한 <code>가변값</code>이며 프로퍼티의 집합이다. 클래스에 정의된 멤버대로 객체를 생성하는 자바같은 경우 객체가 생성된 이후 멤버를 추가할 수 없다. 하지만, 자바스크립트는 객체 생성 이후에도 프로퍼티를 추가하거나 삭제하는 등 변경할 수 있다. 따라서, 깊은 복사를 통해 객체를 복사하지 않으면 객체 사이의 관계가 생성되어 예기치 못한 오류가 발생할 수 있다.</p>
<hr>
<h1 id="얕은-복사란">얕은 복사란?</h1>
<p>얕은 복사란 참조형 타입의 값의 바로 아래 단계의 값만 복사하는 방법이다.</p>
<pre><code class="language-javascript">var obj1 = {
  a: 1,
  b: {
    c: 2,
  },
};
var obj2 = { ...obj1 };
console.log(obj1 === obj2);    // false
console.log(obj1.b === obj2.b);    // true</code></pre>
<p>두 개의 객체는 <code>다른 주소</code>를 가지고 있지만, 객체 안 프로퍼티는 <code>동일한 주소</code>를 가지고 있다. 쉽게 말하면 <code>{}</code>이 껍데기는 새로 생성된 객체이며 새로운 주소를 갖게 되었고 spread 연산자로 풀어진 프로퍼티들은 처음 선언된 obj1의 프로퍼티들이 사용되었다.</p>
<hr>
<h2 id="objectassign">Object.assign()</h2>
<p>assign은 <code>할당</code>이라는 뜻을 가지고 있으며 객체와 객체를 합쳐주는 메서드다.</p>
<pre><code class="language-javascript">var obj1 = {
  a: 10,
  b: {
    c: &#39;abc&#39;,
  },
};
var obj2 = Object.assign({},obj1);
obj2.a = 20;
obj2.b.c = &#39;def&#39;;

console.log(obj1);    // { a: 10, b: {c:&quot;def&quot;} }
console.log(obj2);    // { a: 20, b: {c:&quot;def&quot;} }</code></pre>
<ul>
<li>첫 번쨰 인자로 <code>{}</code> 빈 객체가 들어갔기 떄문에 껍데기가 obj1과 다른 객체가 반환될 것이다.</li>
<li>obj1의 내용을 <code>{}</code> 빈 객체 안에 복사해서 집어놓고 반환한다.</li>
<li>프로퍼티 a는 기본형 타입의 값이 들어있기 때문에 변경하면 obj2의 a 프로퍼티만 변경된다.</li>
<li>프로퍼티 b는 참조형 타입의 값이 들어있기 때문에 변경하면 obj1, obj2의 b가 가진 주소값이 동일하기 때문에 둘 다 변경된다.</li>
</ul>
<p><a href="https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#%EA%B9%8A%EC%9D%80_%EB%B3%B5%EC%82%AC_%EC%A3%BC%EC%9D%98%EC%A0%90">mdn</a>에 따르면 Object.assign()은 깊은 복사를 해주지 않는다고 나와있다.</p>
<hr>
<h2 id="for--in">for ... in</h2>
<pre><code class="language-javascript">var copyShallo = function (obj) {
  var result = {};
  for (var prop in obj) {
    result[prop] = obj[prop];
  }
  return result;
};</code></pre>
<p>깊은 복사에서 사용했던 함수와 달리 재귀를 사용하지 않았다. 단순히 첫 번째 프로퍼티만 새로 만든 result 객체에 담는 형태이다.</p>
<hr>
<h1 id="정리">정리</h1>
<p><code>얕은 복사</code>는 한 단계까지만 복사하고, <code>깊은 복사</code>는 객체에 중첩된 객체까지 모두 복사한다. <code>얕은 복사</code>와 <code>깊은 복사</code> 모두 복사한 대상에 대해서 새로운 객체를 생성하여 기존 객체에는 영향을 주지 않는다. 하지만 <code>얕은 복사</code>와 <code>깊은 복사</code>는 어느 수준까지 복사하느냐의 차이를 가진다. <code>얕은 복사</code>를 하면 <code>한 단계만 복사</code>하기 때문에 중첩된 객체에 대해서는 <code>서로 영향</code>을 주고, <code>깊은 복사</code>는 중첩된 객체 역시 <code>별개의 값</code>으로서 <code>서로 영향을 주지 않는다.</code></p>
<p>출처 - <a href="https://pozafly.github.io/javascript/shallo-copy-and-deep-copy/">https://pozafly.github.io/javascript/shallo-copy-and-deep-copy/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[정규화]]></title>
            <link>https://velog.io/@nawhes_joo/%EC%A0%95%EA%B7%9C%ED%99%94</link>
            <guid>https://velog.io/@nawhes_joo/%EC%A0%95%EA%B7%9C%ED%99%94</guid>
            <pubDate>Wed, 24 Apr 2024 02:12:28 GMT</pubDate>
            <description><![CDATA[<h1 id="정규화란">정규화란?</h1>
<blockquote>
<p>정규화(Normalization)의 기본 목표는 <code>테이블 간에 중복된 데이터를 허용하지 않는다는 것</code>이다. 중복된 데이터를 허용하지 암으로써 <code>무결성(Integrity)</code>를 유지할 수 있으며, <code>DB의 저장 용량 역시 줄일 수 있다.</code></p>
</blockquote>
<ul>
<li><p><code>갱신 이상 (Modification Anomaly)</code>
중복된 데이터 중 일부를 갱신할 때 의도치 않은 데이터가 갱신됨으로써 생기는 데이터의 불일치</p>
</li>
<li><p><code>삽입 이상 (Insertion Anomaly)</code>
새 데이터를 삽입할 때 의도치 않은 데이터가 삽입됨으로써 생기는 데이터의 불일치</p>
</li>
<li><p><code>삭제 이상 (Deletion Anomaly)</code>
데이터를 삭제할 때 의도치 않은 데이터까지 삭제됨으로써 생기는 데이터의 불일치</p>
</li>
</ul>
<hr>
<h1 id="제1-정규화">제1 정규화</h1>
<blockquote>
<p>테이블의 컬럼이 <code>원자값(Atomic Value, 하나의 값)</code>을 갖도록 테이블을 분해하는 것</p>
</blockquote>
<p>예시)
<img src="https://velog.velcdn.com/images/nawhes_joo/post/3a46083e-b4dc-4a88-a6c0-0e28741fc16b/image.png" alt="">
위 테이블에서 추신수와 박세리는 여러 개의 취미를 가지고 있기 때문에 제1정규형을 만족하지 못하고 있다. 그렇기 때문에 이를 제1정규화하여 분해할 수 있다.
<img src="https://velog.velcdn.com/images/nawhes_joo/post/418cf976-7730-4edc-a282-659fb7019a8b/image.png" alt=""></p>
<hr>
<h1 id="제2-정규화">제2 정규화</h1>
<blockquote>
<p>제1 정규화를 진행한 테이블에 대해 <code>완전 함수 종속</code>을 만족하도록 테이블을 분해하는 것이다.
여기서 <code>완전 함수 종속</code>이라는 것은 <code>기본키의 부분집합이 결정자가 되어선 안된다는 것</code>을 의미한다.</p>
</blockquote>
<p>예시)
<img src="https://velog.velcdn.com/images/nawhes_joo/post/9c4f499a-2850-4f2e-955a-e103fb5b6091/image.png" alt="">
이 테이블에서 기본키는 (학생번호, 강좌이름)으로 복합키이다. 그리고 (학생번호, 강좌이름)인 기본키는 성적을 결정하고 있다. (학생번호, 강좌이름) --&gt; (성적)</p>
<p>그런데 여기서 강의실이라는 컬럼은 기본키의 부분집합인 강좌이름에 의해 결정될 수 있다. (강좌이름) --&gt; (강의실)</p>
<p>즉, 기본키(학생번호, 강좌이름)의 부분키인 강좌이름이 결정자이기 때문에 위 테이블은 아래와 같이 기존 테이블에서 강의실을 분해하여 별도의 테이블로 관리하여 제2 정규형을 만족시킬 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/446e6a5f-7239-4baa-97d7-80c1ee71a7f5/image.png" alt=""></p>
<hr>
<h1 id="제3-정규화">제3 정규화</h1>
<blockquote>
<p>제2 정규화를 진행한 테이블에 대해 <code>이행적 종속</code>을  없애도록 테이블을 분해하는 것이다.</p>
</blockquote>
<p>이행적 종속이란, A-&gt;B, B-&gt;C가 성립할 때 A-&gt;C가 성립되는 것을 의미한다.</p>
<p>예시)
<img src="https://velog.velcdn.com/images/nawhes_joo/post/f5be16f7-4701-4b36-9fed-e9e176e05333/image.png" alt="">
기존의 테이블에서 학생 번호는 강좌 이름을 결정하고 있고, 강좌 이름은 수강료를 결정하고 있다. 그렇기 때문에 이를 (학생 번호, 강좌 이름, 수강료) 테이블로 분해해야 한다.</p>
<p>이행적 종속을 제거하는 이유는 비교적 간단하다. 예를 들어 501번 학생이 수강하는 강좌가 스포츠경영학으로 변경되었을 때, 이행적 종속이 존재하다면 501번의 학생은 스포츠경영학 수업을 20000원의 수강료로 듣게 된다. 물론 강좌 이름에 맞게 수강료를 변경할 수 있지만, 이러한 번거로움을 해결하기 위해 제3 정규화를 진행하는 것이다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/2c83c657-2578-4909-86ca-342ab48103e8/image.png" alt="">
학생 번호를 통해 강좌 이름을 참조하고, 강좌 이름으로 수강료를 참조하도록 테이블을 분해한다.</p>
<hr>
<h1 id="bcnf-정규화">BCNF 정규화</h1>
<blockquote>
<p>제3 정규화를 진행한 테이블에 대해 <code>모든 결정자</code>가 <code>후보키</code>가 되도록 테이블을 분해하는 것이다.</p>
</blockquote>
<p>예시)
<img src="https://velog.velcdn.com/images/nawhes_joo/post/4664d919-d8f5-442e-879c-1b1b2691b5c6/image.png" alt="">
특강수강 테이블에서 기본키는 (학생 번호, 특강 이름)이다. 그리고 기본키 (학생 번호, 특강 이름)는 교수를 결정하고 있다. 또한 여기서 교수는 특강 이름을 결정하고 있다.
그러나 교수가 특강 이름을 결정하는 결정자이지만, 후보키가 아니다. 그렇기 때문에 BCNF 정규화를 만족시키기 위해 테이블을 분해해야 한다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/74e3b458-fd00-47fe-84d4-dc4b55bf18de/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript - 맵과 셋]]></title>
            <link>https://velog.io/@nawhes_joo/JavaScript-%EB%A7%B5%EA%B3%BC-%EC%85%8B</link>
            <guid>https://velog.io/@nawhes_joo/JavaScript-%EB%A7%B5%EA%B3%BC-%EC%85%8B</guid>
            <pubDate>Mon, 15 Apr 2024 00:53:51 GMT</pubDate>
            <description><![CDATA[<h1 id="map">Map</h1>
<p>맵(map)은 키가 있는 데이터를 저장한다는 점에서 <code>객체</code>와 유사하다. 다만, <code>맵</code>은 키에 다양한 자료형을 허용한다는 차이가 있다.</p>
<p>맵에는 다음과 같은 주요 프로퍼티가 있다.</p>
<ul>
<li><code>new Map()</code> - 맵을 만든다.</li>
<li><code>map.set(key, value)</code> - <code>key</code>를 이용해 <code>value</code>를 저장</li>
<li><code>map.get(key)</code> - <code>key</code>에 해당하는 값을 반환. <code>key</code>가 존재하지 않으면 <code>undefined</code>를 반환</li>
<li><code>map.has(key)</code> - <code>key</code>가 존재하면 <code>true</code>, 존재하지 않으면 <code>false</code>를 반환</li>
<li><code>map.delete(key)</code> - <code>key</code>에 해당하는 값을 삭제</li>
<li><code>map.clear()</code> - 맵 안의 모든 요소를 제거</li>
<li><code>map.size</code> - 요소의 개수를 반환</li>
</ul>
<hr>
<p>예시:</p>
<pre><code class="language-javascript">let map = new Map();

map.set(&#39;1&#39;, &#39;str1&#39;);    // 문자형 키
map.set(1, &#39;num1&#39;);     // 숫자형 키
map.set(true, &#39;bool1&#39;); // 불린형 키

alert( map.get(1) );    // &#39;num1&#39;
alert( map.get(&#39;1&#39;) );// &#39;str1&#39;

alert( map.size );    // 3</code></pre>
<p>맵은 객체와 달리 키를 문자형으로 반환하지 않는다. 키엔 자료형 제약이 없다.</p>
<hr>
<blockquote>
<p>맵은 키로 객체를 허용한다.</p>
</blockquote>
<pre><code class="language-javascript">let john = { name: &quot;John&quot; };

let visitsCountMap = new Map();

visitsCountMap.set(john, 123);

alert( visitsCountMap.get(john) ); // 123</code></pre>
<p>객체를 키로 사용할 수 있다는 점은 <code>맵</code>의 가장 중요한 기능 중 하나이다. <code>객체</code>에는 문자열 키를 사용할 수 있다. 하지만 객체 키는 사용할 수 없다.</p>
<p>객체형 키를 <code>객체</code>에 써보자.</p>
<pre><code class="language-javascript">let john = { name: &quot;John&quot; };

let visitsCountObj = {};

visitsCountObject[john] = 123;

alert( visitsCountObj[&quot;[object Object]&quot;] ); //123</code></pre>
<p><code>visitsCountObj</code>는 객체이기 때문에 모든 키를 문자형으로 변환시킨다. 이 과정에서 <code>john</code>은 문자형으로 변환되어 <code>&quot;[object Object]&quot;</code>가 된다.</p>
<hr>
<h1 id="맵의-요소에-반복-작업하기">맵의 요소에 반복 작업하기</h1>
<p>다음 세 가지 메서드를 사용해 <code>맵</code>의 각 요소에 반복 작업을 할 수 있다.</p>
<ul>
<li><code>map.keys()</code> - 각 요소의 키를 모은 반복 가능한(iterable, 이터러블) 객체를 반환한다.</li>
<li><code>map.values()</code> - 각 요소의 값을 모은 이터러블 객체를 반환한다.</li>
<li><code>map.entries()</code> - 요소의 <code>[키, 값]</code>을 한 쌍으로 하는 이터러블 객체를 반환한다. 이 이터러블 객체는 <code>for..of</code> 반복문의 기초로 쓰인다.</li>
</ul>
<p>예시: </p>
<pre><code class="language-javascript">let recipeMap = new Map([
  [&#39;cucumber&#39;, 500],
  [&#39;tomatoes&#39;, 350],
  [&#39;onion&#39;,    50]
]);

// 키(vegetable)를 대상으로 순회
for (let vegetable of recipeMap.keys()) {
    alert(vegetable);    // cucumber, tomatoes, onion
}

// 값(amount)을 대상으로 순회
for (let amount of recipeMap.values()) {
    alert(amount);    // 500, 350, 50
}

// [키, 값] 쌍을 대상으로 순회
for (let entry of recipeMap) { // recipeMap.entries()와 동일
    alert(entry);    // cucumber,500 ...
}</code></pre>
<hr>
<h1 id="objectentries-객체를-맵으로-바꾸기">Object.entries: 객체를 맵으로 바꾸기</h1>
<p>각 요소가 키-값 쌍인 배열이나 이터러블 객체를 초기화 용도로 <code>맵</code>에 전달해 새로운 <code>맵</code>을 만들 수 있다.</p>
<pre><code class="language-javascript">//각 요소가 [키, 값] 쌍인 배열
let map = new Map([
  [&#39;1&#39;, &#39;str1&#39;],
  [1, &#39;num1&#39;],
  [true, &#39;bool1&#39;]
  ]);

alert(map.get(&#39;1&#39;) ); // str1</code></pre>
<p>평범한 객체를 가지고 <code>맵</code>을 만들고 싶다면 내장 메서드 Object.entries(obj)를 활용해야 한다.
이 메서드는 객체의 키-값 쌍을 요소(<code>[key, value]</code>)로 가지는 배열을 반환한다.</p>
<pre><code class="language-javascript">let obj = {
  name: &quot;John&quot;,
  age: 30
};

let map = new Map(Object.entries(obj)
alert( map.get(&#39;name&#39;) ); // John</code></pre>
<p><code>Object.entries</code>를 사용해 객체 <code>obj</code>를 배열 <code>[ [&quot;name&quot;, &quot;John&quot;], [&quot;age&quot;, 30] ]</code>로 바꾸고, 이 배열을 이용해 사로운 <code>맵</code>을 만들었다.</p>
<hr>
<h1 id="objectfromentries-맵을-객체로-바꾸기">Object.fromEntries: 맵을 객체로 바꾸기</h1>
<p><code>Object.fromeEntries</code>를 사용하여 맵을 객체로 바꿀 수 있다.
이 메서드는 각 요소가 <code>[키, 값]</code>쌍인 배열을 객체로 바꿔준다.</p>
<pre><code class="language-javascript">let prices = Object.fromEntries([
  [&#39;banana&#39;, 1],
  [&#39;orange&#39;, 2],
  [&#39;meat&#39;, 4]
]);

// now prices = { banana: 1, orange: 2, meat: 4 }

alert(prices.orange}; // 2</code></pre>
<p><code>Object.fromEntries</code>를 사용해 <code>맵</code>을 객체로 바꿔보자.</p>
<p>자료가 <code>맵</code>에 저장되어있는데, 서드파티 코드에서 자료를 객체형태로 넘겨받길 원할 때 이 방법을 사용할 수 있다.</p>
<pre><code class="language-javascript">let map = new Map();
map.set(&#39;banana&#39;, 1);
map.set(&#39;orange&#39;, 2);
map.set(&#39;meat&#39;, 4);

let obj = Object.fromEntries(map.entries()); // 맵을 일반 객체로 변환(*)

// 맵이 겍체가 되었다.

// obj = { banana: 1. orange: 2, meat: 4 }

alert(obj.orange); // 2</code></pre>
<p><code>map.entries()</code>를 호출하면 맵의 <code>[키, 값]</code>을 요소로 가지는 이터러블을 반환한다. <code>Object.fromEntries</code>를 사용하기 위해 딱 맞는 형태이다.</p>
<p><code>(*)</code>로 표시한 줄을 좀 더 짧게 줄이는 것도 가능하다.</p>
<pre><code class="language-javascript">let obj = Object.fromEntries(map); // .entries()를 생략함</code></pre>
<p><code>Object.fromEntries</code>는 인수로 이터러블 객체를 받기 때문에 짧게 줄인 코드도 이전 코드와 동일하게 작동한다. 꼭 배열을 전달해줄 필요는 없다. 그리고 <code>map</code>에서의 일반적인 반복은 <code>map.entries()</code>를 사용했을 때와 같은 키-값 쌍을 반환한다. 따라서 <code>map</code>과 동일한 키-값을 가진 일반 객체를 얻게 된다.</p>
<hr>
<h1 id="셋">셋</h1>
<p><code>셋(set)</code>은 중복을 허용하지 않는 값을 모아놓은 틀별한 컬렉션이다. 셋에 키가 없는 값이 저장된다.</p>
<p>주요 메서드는 다음과 같다.</p>
<ul>
<li><code>new Set(iterable)</code> - 셋을 만든다. <code>이터러블</code> 객체를 전달받으면(대게 배열을 전달받음) 그 안의 값을 복사해 셋에 넣어준다.</li>
<li><code>set.add(value)</code> - 값을 추가하고 셋 자신을 반환한다.</li>
<li><code>set.delete(value)</code> - 값을 제거한다. 호출 시점에 셋 내에 값이 있어서 제거에 성공하면 <code>true</code>, 아니면 <code>false</code>를 반환한다.</li>
<li><code>set.has(value)</code> - 셋 내에 값이 존재하면 <code>true</code>, 아니면 <code>false</code>를 반환한다.</li>
<li><code>set.clear()</code> - 셋을 비운다.</li>
<li><code>set.size</code> - 셋에 몇 개의 값이 있는지 세어준다.</li>
</ul>
<p>셋 내에 동일한 값(value)이 있다면 <code>set.add(value)</code>을 아무리 많이 호출하더라도 아무런 반응이 없을 것이다. 셋 내의 값에 중복이 없는 이유가 바로 이 때문이다.</p>
<p>방문자 방명록을 만든다고 가정해보자. 한 방문자가 여러 번 방문해도 반문자를 중복해서 기록하지 않겠다고 결정을 내린 상황이다. 즉, 한 방문자는 &#39;단 한 번만 기록&#39;되어야 한다.</p>
<p>이때 적합한 자료구조가 바로 <code>셋</code>이다.</p>
<pre><code class="language-javascript">let set = new Set();

let john = { name: &quot;John&quot; };
let pete = { name: &quot;Pete&quot; };
let mary = { name: &quot;Mary&quot; };

// 어떤 고객(john, mary)은 여러 번 방문할 수 있다.
set.add(john);
set.add(pete);
set.add(mary);
set.add(john);
set.add(mary);

// 셋에는 유일무이한 값만 저장된다.
alert( set.size );    // 3

for (let user of set) {
  alert( user.name );    // John, Pete, Mary 순으로 출력
}</code></pre>
<p><code>셋</code> 대신 배열을 사용하여 방문자 정보를 저장한 후, 중복 값 여부는 배열 메서드인 <code>arr.find</code>를 이용해 확인할 수도 있다. 하지만 <code>arr.find</code>는 배열 내 요소 전체를 뒤져 중복 값을 찾기 때문에, 셋보다 성능 면에서 떨어진다. 반면, <code>셋</code>은 값의 유일무이함을 확인하는데 최적화되어있다.</p>
<hr>
<h1 id="셋의-값에-반복-작업하기">셋의 값에 반복 작업하기</h1>
<p><code>for..of</code>나 <code>forEach</code>를 사용하면 셋의 값을 대상으로 반복 작업을 수행할 수 있다.</p>
<pre><code class="language-javascript">let set = new Set([&quot;oranges&quot;, &quot;apples&quot;, &quot;bananas&quot;]);

for (let value of set) alert( value );

// forEach를 사용해도 동일하게 동작한다.
set.forEach((value, valueAgain, set) =&gt; {
  alert( value );
});</code></pre>
<p><code>forEach</code>에 쓰인 콜백 함수는 세 개의 인수를 받는데, 첫 번째는 <code>값</code>, 두 번째도 같은 값인 <code>valueAgain</code>을 받고 있다. 세 번째는 목표하는 객체(셋)이다. 동일한 값이 인수에 두 번 등장하고 있다.</p>
<p>이렇게 구현된 이유는 <code>맵</code>과의 호환성 때문이다. <code>맵</code>의 <code>forEach</code>에 쓰인 콜백이 세 개의 인수를 받을 때를 위해서이다. <code>맵</code>을 <code>셋</code>으로 혹은 <code>셋</code>을 <code>맵</code>으로 교체하기가 쉽다.</p>
<p><code>셋</code>에서도 <code>맵</code>과 마찬가지로 반복 작업을 위한 메서드가 있다.</p>
<ul>
<li><code>set.keys()</code> - 셋 내의 모든 값을 포함하는 이터러블 객체를 반환한다.</li>
<li><code>set.values()</code> - <code>set.keys</code>와 동일한 작업을 한다. <code>맵</code>과의 호환성을 위해 만들어진 메서드이다.</li>
<li><code>set.entries()</code> - 셋 내의 각 값을 이용해 만든 <code>[value, value]</code> 배열을 포함하는 이터러블 객체를 반환한다. <code>맵</code>과의 호환성을 위해 만들어졌다.</li>
</ul>
<hr>
<h1 id="요약">요약</h1>
<p><code>맵</code>은 키가 있는 값이 저장된 컬렉션이다.</p>
<p>주요 메서드와 프로퍼티 : </p>
<ul>
<li><code>new Map([iterable])</code> - 맵을 만든다. <code>[key, value]</code> 쌍이 있는 <code>iterable</code>(예: 배열)을 선택적으로 넘길 수 있는데, 이때 넘긴 이터러블 객체는 맵 초기화에 사용된다.</li>
<li><code>map.set(key, value)</code> - 키를 이용해 값을 저장한다.</li>
<li><code>map.get(key)</code> - 키에 해당하는 값을 반환한다. <code>key</code>가 존재하지 않으면 <code>undefined</code>를 반환한다.</li>
<li><code>map.has(key)</code> - 키가 있으면 <code>true</code>, 없으면 <code>false</code>를 반환한다.</li>
<li><code>map.delete(key)</code> - 키에 해당하는 값을 삭제한다.</li>
<li><code>map.clear()</code> - 맵 안의 모든 요소를 제거한다.</li>
<li><code>map.size</code> - 요소의 개수를 반환한다.</li>
</ul>
<p>일반적인 <code>객체</code>와의 차이점:</p>
<ul>
<li>키의 타입에 제약이 없다. 객체도 키가 될 수 있다.</li>
<li><code>size</code> 프로퍼티 등의 유용한 케서드나 프로퍼티가 있다.</li>
</ul>
<hr>
<p><code>셋</code>은 중복이 없는 값을 저장할 때 쓰이는 컬렉션이다.</p>
<p>주요 메서드와 프로퍼티 :</p>
<ul>
<li><code>new Set([iterable])</code> - 셋을 만든다. <code>iterable</code> 객체를 선택적으로 전달받을 수 있는데(대개 배열을 전달받음), 이터러블 객체 안의 요소는 셋을 초기화하는데 쓰인다.</li>
<li><code>set.add(value)</code> - 값을 추가하고 셋 자신을 반환한다. 셋 내에 이미 <code>value</code>가 있는 경우 아무런 작업을 하지 않는다.</li>
<li><code>set.delete(value)</code> - 셋 내에 값이 존재하면 <code>true</code>, 아니면 <code>false</code>를 반환한다.</li>
<li><code>set.clear()</code> - 셋을 비운다.</li>
<li><code>set.size</code> - 셋에 몇 개의 값이 있는지 세어준다.</li>
</ul>
<p><code>맵</code>과 <code>셋</code>에 반복 작업을 할 땐, 해당 컬렉션에 요소나 값을 추가한 순서대로 반복 작업이 수행된다. 따라서 이 두 컬렉션은 정렬이 되어있지 않다고 할 수 없다. 그렇지만 컬렉션 내 요소나 값을 재정렬하거나 (배열에서 인덱스를 이용해 요소를 가져오는 것처럼) 숫자를 이용해 특정 요소나 값을 가지고 오는 것은 불가능하다.</p>
<p>※ 출처 - 모던 JavasScript (<a href="https://ko.javascript.info/map-set#ref-1160">https://ko.javascript.info/map-set#ref-1160</a>)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[JavaScript - 제너레이터]]></title>
            <link>https://velog.io/@nawhes_joo/JavaScript-%EC%A0%9C%EB%84%88%EB%A0%88%EC%9D%B4%ED%84%B0</link>
            <guid>https://velog.io/@nawhes_joo/JavaScript-%EC%A0%9C%EB%84%88%EB%A0%88%EC%9D%B4%ED%84%B0</guid>
            <pubDate>Fri, 29 Mar 2024 05:40:52 GMT</pubDate>
            <description><![CDATA[<h1 id="제너레이터란">제너레이터란?</h1>
<p>일반 함수는 하나의 값(혹은 0개의 값)만을 반환합니다.</p>
<blockquote>
<p>하지만 제너레이터(generator)를 사용하면 여러 개의 값을 필요에 따라 하나씩 변환(yield)할 수 있습니다.</p>
</blockquote>
<p>제너레이터와 이터러블 객체를 함께 사용하면 손쉽게 데이터 스트림을 만들 수 있습니다.</p>
<hr>
<h1 id="제너레이터-함수">제너레이터 함수</h1>
<p>제너레이터를 만들려면 &#39;제너레이터 함수&#39;라 불리는 특별한 문법 구조, <code>function*</code> 이 필요합니다.</p>
<p>예시 )</p>
<pre><code class="language-javascript">function* generateSequence(){
  yield 1;
  yield 2;
  return 3;
}</code></pre>
<hr>
<p>제너레이터 함수는 일반 함수와 동작 방식이 다릅니다. 제너레이터 함수를 호출하면 코드가 실행되지 않고, 대신 실행을 처리하는 특별 객체, <code>제너레이터 객체</code>가 변환됩니다.</p>
<p>예시)</p>
<pre><code class="language-javascript">function* generateSequence(){
  yield 1;
  yield 2;
  return 3;
}

// &#39;제너레이터 함수&#39;는 &#39;제너레이터 객체&#39;를 생성합니다.
let generator = generateSequence();
console.log(generator); // [Object Generator]</code></pre>
<p>함수 본문 코드는 아직 실행되지 않았습니다.</p>
<hr>
<p><code>next()</code>는 제너레이터의 주요 메서드입니다.</p>
<p><code>next()</code>를 호출하면 가장 가까운 <code>yield &lt;value&gt;</code>문을 만날 때까지 실행이 지속됩니다.(value를 생략할 수도 있는데, 이 경우엔 <code>undefined</code>가 됩니다)</p>
<p>이후, <code>yield &lt;value&gt;</code>문을 만나면 실행이 멈추고 산출하고자 하는 값인 <code>value</code>가 바깥 코드에 반환됩니다.</p>
<p><code>next()</code>는 항상 아래 두 프로퍼티를 가진 객체를 반환합니다.</p>
<ul>
<li><code>value</code> : 산출 값</li>
<li><code>done</code> : 함수 코드 실행이 끝났으면 <code>true</code>, 아니라면 <code>false</code></li>
</ul>
<hr>
<p>제너레이터를 만들고 첫 번째 산출 값을 받는 예시를 살펴봅시다.</p>
<pre><code class="language-javascript">function* generateSequence() {
  yield 1;
  yield 2;
  return 3;
}

let generator = generateSequence();

let one = generator.next();

console.log(JSON.stringify(one)); // {value: 1, done: false}</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/9e68fb4c-cac8-4415-a2fc-1e49cafcd323/image.png" alt=""></p>
<p>첫 번째 값만 받았으므로 함수 실행은 두 번째 줄에서 멈췄습니다.</p>
<pre><code class="language-javascript">let two = generator.next();

console.log(JSON.stringify(two)); // {value: 2, done: false}</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/3ded4e98-934b-4c18-a833-6d042fd4a1e2/image.png" alt=""></p>
<p><code>generator.next()</code>를 다시 호출해봅시다. 실행이 재개되고 다음 <code>yield</code>를 반환합니다.</p>
<hr>
<pre><code class="language-javascript">let three = generator.next();

console.log(JSON.stringify(three)); // {value: 3, done: true}</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/2652e350-c082-42e1-83da-3999b8cf743e/image.png" alt=""></p>
<p><code>generator.next()</code>를 또 호출하면 실행은 <code>return</code>문에 다다르고 함수가 종료됩니다.</p>
<p>마지막 결과인 <code>value : 3</code>과 <code>done:trye</code>를 통해 종료된 것을 확인할 수 있습니다.</p>
<hr>
<pre><code class="language-javascript">let four = generator.next();

console.log(JSON.stringify(four));</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/a259e086-e54e-4bf0-961d-1bfd8f01b67c/image.png" alt=""></p>
<p>제너레이터가 종료되었기 때문에 지금 상태에선 <code>generator.next()</code>를 호출해도 아무 소용이 없습니다.</p>
<p><code>generator.next()</code>를 여러번 호출해도 객체 <code>{done: true}</code>가 반환될 뿐입니다.</p>
<hr>
<h1 id="제너레이터와-이터러블">제너레이터와 이터러블</h1>
<p><code>next()</code> 메서드를 보면서 짐작했듯이, 제너레이터는 이터러블입니다.</p>
<p>따라서 <code>for..of</code> 반복문을 사용해 값을 얻을 수 있습니다.</p>
<pre><code class="language-javascript">function* generateSequence(){
  yield 1;
  yield 2;
  return 3;
}

let generator = generateSequence();

for(let value of generator){
  console.log(value);
}</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/209ef3bc-e9fa-42f8-a49e-94fbc149f130/image.png" alt=""></p>
<p>1, 2만 출력되고 3은 출력되지 않습니다.</p>
<p>그 이유는 <code>for..of</code> 이터레이션이 <code>done: true</code> 일 때 마지막 <code>value</code>를 무시하기 때문입니다. 그러므로 <code>for..of</code>를 사용했을 때 모든 값이 출력되길 원한다면 <code>yield</code>로 값을 반환해야 합니다.</p>
<hr>
<pre><code class="language-javascript">function* generateSequence(){
  yield 1;
  yield 2;
  yield 3;
}

let generator = generateSequence();

for(let value of generator){
  console.log(value);
}</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/7f706cf1-9a0f-4d4e-89ea-f3697d35dc20/image.png" alt=""></p>
<p>모든 값이 정상적으로 반환되는 모습</p>
<hr>
<p>제너레이터는 이터러블 객체이므로 제너레이터에도 전개 구문(...)같은 관련 기능을 사용할 수 있습니다.</p>
<pre><code class="language-javascript">function* generateSequence(){
  yield 1;
  yield 2;
  yield 3;
}

let sequence = [0, ...generateSequence()];

console.log(sequence)</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/7af11143-4502-4442-9e4c-59f38c8c7bc0/image.png" alt=""></p>
<p>위 예시에서 <code>...generateSequence()</code>는 반복 가능한 제너레이터 객체를 배열 요소로 바꿔줍니다.</p>
<hr>
<h1 id="이터러블-대신-제너레이터-사용하기">이터러블 대신 제너레이터 사용하기</h1>
<p><code>from..to</code> 사이에 있는 값을 반환하는 반복 가능 객체, <code>range</code>를 만들어 보았습니다.</p>
<pre><code class="language-javascript">let range = {
  from: 1,
  to: 5,

  // for..of 최초 호출 시, Symbol.iterator가 호출됩니다.
  [Symbol.iterator]() {
    // Symbol.iterator는 이터레이터 객체를 반환합니다.
    // for..of는 반환된 이터레이터 객체만을 대상으로 동작하는데, 이때 다음 값도 정해집니다.
    return {
      current: this.from,
      last: this.to,

      // for..of 반복문에 의해 각 이터레이션마다 next()가 호출됩니다.
      next() {
        // next()는 객체 형태의 값, {done:.., value :...}을 반환해야 합니다.
        if (this.current &lt;= this.last) {
          return { done: false, value: this.current++ };
        } else {
          return { done: true };
        }
      }
    };
  }
};

// 객체 range를 대상으로 하는 이터레이션은 range.from과 range.to 사이의 숫자를 출력합니다.
console.log([...range]); // 1,2,3,4,5</code></pre>
<p><code>Symbol.iterator</code> 대신 제너레이터 함수를 사용하면, 제너레이터 함수로 반복이가능합니다.</p>
<p>같은 <code>range</code>이지만, 좀 더 압축된 <code>range</code>를 살펴봅시다.</p>
<pre><code class="language-javascript">let range = {
  from: 1,
  to: 5,

  *[Symbol.iterator]() { // [Symbol.iterator]: function*()를 짧게 줄임
    for(let value = this.from; value &lt;= this.to; value++){
      yield value;
    }
  }
};

console.log([...range]);</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/9189c920-eb00-429d-b18c-fe2ff4584c2e/image.png" alt=""></p>
<p><code>range[Symbol.iterator]()</code>는 제너레이터를 반환하고, 제너레이터 메서드는 <code>for..of</code>가 동작하는데 필요한 사항(아래 설명)을 충족시키므로 예시가 잘 동작합니다.</p>
<ul>
<li><code>.next()</code> 메서드가 있음</li>
<li>반환 값의 형태는 <code>{value: ..., done: true/false}</code> 이어야 함</li>
</ul>
<p>이렇게 이터러블 객체 대신 제너레이터를 사용할 수 있는 것은 우연이 아닙니다. 제너레이터는 이터레이터를 어떻게 하면 쉽게 구현할지를 염두에 두며 자바스크립트에 추가되었습니다.</p>
<p>제너레이터를 사용해 구현한 예시는 이터러블을 사용해 구현한 기존 예시보다 훨씬 간결합니다. 그리고 동일한 기능을 제공합니다.</p>
<hr>
<h1 id="제너레이터-컴포지션">제너레이터 컴포지션</h1>
<p>제너레이터 컴포지션(generator composition)은 제너레이터 안에 제너레이터를 &#39;임베딩(embedding, composing)&#39;할 수 있게 해주는 제너레이터의 특별 기능입니다.</p>
<p>먼저, 연속된 숫자를 생성하는 제너레이터 함수를 만들어보겠습니다.</p>
<pre><code class="language-javascript">function* generateSequence(start, end) {
  for(let i = start; i &lt;= end; i++) yield i;
}</code></pre>
<p>그리고 이 함수를 기반으로 좀 더 복잡한 값을 연속해서 생성하는 함수를 만들어봅시다. </p>
<blockquote>
<ul>
<li>숫자 0부터 9까지 생성(문자 코드 48부터 57까지)</li>
</ul>
</blockquote>
<ul>
<li>알파벳 대문자 A부터 Z까지 생성(문자 코드 65부터 90까지)</li>
<li>알파벳 소문자 a부터 z까지 생성(문자코드 97부터 122까지)</li>
</ul>
<p>이런 규칙을 충족하는 연속 값은 비밀번호를 만들 때 응용할 수 있습니다.(특수 문자도 추가 가능)</p>
<p>일반 함수로는 여러 개의 함수를 만들고 그 호출 결과를 어딘가에 저장한 후 다시 그 결과들을 조합해야 원하는 기능을 구현할 수 있습니다.</p>
<p>하지만 제너레이터의 특수 문법 <code>yield*</code>를 사용하면 제너레이터를 다른 제너레이터에 &#39;끼워 넣을 수&#39; 있습니다.</p>
<p><strong>컴포지션을 적용한 제너레이터</strong></p>
<pre><code class="language-javascript">function* generateSequence(start, end) {
  for(let i = start; i &lt;= end; i++) yield i;
}

function* generatePasswordCodes() {

  // 0~9
  yield* generateSequence(48, 57);

  // A~Z
  yield* generateSequence(65, 90);

  // a~z
  yield* generateSequence(97, 122);

}

let str = &#39;&#39;;

for(let code of generatePasswordCodes()) {
  str += String.fromCharCode(code);
}

console.log(str);</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/6a31efb7-673c-48ae-b68f-df99b27b1c64/image.png" alt=""></p>
<p>순서대로 0<del>9, A</del>Z, a~z가 출력되는 모습</p>
<p><code>yield*</code> 지시자는 실행을 다른 제너레이터에 위임합니다(delegate). 여기서 &#39;위임&#39;은 <code>yield* gen</code>이 제너레이터 <code>gen</code>을 대상으로 반복을 수행하고, 산출 값들을 바깥으로 전달한다는 것을 의미합니다. 마치 외부 제너레이터에 의해 값이 선출된 것처럼 말이죠.</p>
<p>중첩 제너레이터의 코드를 직접 써줘도 결과는 같습니다.</p>
<pre><code class="language-javascript">function* generateSequence(start, end) {
  for (let i = start; i &lt;= end; i++) yield i;
}

function* generateAlphaNum() {

  // yield* generateSequence(48, 57);
  for (let i = 48; i &lt;= 57; i++) yield i;

  // yield* generateSequence(65, 90);
  for (let i = 65; i &lt;= 90; i++) yield i;

  // yield* generateSequence(97, 122);
  for (let i = 97; i &lt;= 122; i++) yield i;

}

let str = &#39;&#39;;

for(let code of generateAlphaNum()) {
  str += String.fromCharCode(code);
}

console.log(str); // 0..9A..Za..z</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/5b59a974-dd8c-42db-8f06-a7c3cbcbad03/image.png" alt=""></p>
<p>같은 결과가 나오는 모습을 확인할 수 있습니다.</p>
<p>제너레이터 컴포지션을 사용하면 한 제너레이터의 흐름을 자연스럽게 다른 제너레이터에 삽입할 수 있습니다. 제너레이터 컴포지션을 사용하면 중간 결과 저장 용도의 추가 메모리가 필요하지 않습니다.</p>
<hr>
<h1 id="yield를-사용해-제너레이터-안밖으로-정보-교환하기">&#39;yield&#39;를 사용해 제너레이터 안/밖으로 정보 교환하기</h1>
<p>제너레이터는 값을 생성해주는 특수 문법을 가진 이터러블 객체와 유사했습니다. 그러나 사실 제너레이터는 더 강력하고 유연한 기능을 제공합니다.</p>
<p><code>yield</code>가 양방향 길과 같은 역할을 하기 때문입니다. <code>yield</code>는 결과를 바깥으로 전달할 뿐만 아니라 값을 제너레이터 안으로 전달하기까지 합니다.</p>
<p>값은 안/밖으로 전달하려면 <code>generator/next(arg)</code>를 호출해야 합니다. 이때 인수 <code>arg</code>는 <code>yield</code>의 결과가 됩니다.</p>
<pre><code class="language-javascript">function* gen() {
  // 질문을 제너레이터 밖 코드로 던지고 답을 기다립니다.
  let result = yield &quot;2 + 2 = &quot;; // (*)

  console.log(result);
}

let generator = gen();

let question = generator.next().value;  // &lt;-- yield는 value를 반환합니다.

generator.next(4); // --&gt; 결과를 제너레이터 안으로 전달합니다.</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/546e452d-410f-4020-8a73-d4dbec70a129/image.png" alt=""></p>
<ol>
<li><p><code>generator.next()</code>를 처음 호출할 땐 항상 인수가 없어야 합니다. 인수가 넘어오더라도 무시되어야 합니다.
<code>generator.next()</code>를 호출하면 실행이 시작되고 첫 번째 <code>yield &quot;2+2=?&quot;</code>의 결과가 반환됩니다. 이 시점에는 제너레이터가 <code>(*)</code>로 표시한 줄에서 실행을 잠시 멈춥니다.</p>
</li>
<li><p>그 후, 위 그림에서 보듯이 <code>yield</code>의 결과가 제너레이터를 호출하는 외부 코드에 있는 변수, <code>question</code>에 할당됩니다.</p>
</li>
<li><p>마지막 줄, <code>generator.next(4)</code>에서 제너레이터가 다시 시작되고 <code>4</code>는 <code>result</code>에 할당됩니다(<code>let result = 4</code>)</p>
</li>
</ol>
<p>외부 코드에선 <code>next(4)</code>를 즉시 호출하지 않고 있습니다. 제너레이터가 기다려주기 때문에 호출을 나중에 해도 문제가 되지 않습니다.</p>
<p>예시 :</p>
<pre><code class="language-javascript">// 일정 시간이 지난 후에 제너레이터가 다시 시작됨
setTimeout(() =&gt; generator.next(4), 1000);</code></pre>
<hr>
<p>일반 함수와 다르게 제너레이터와 외부 호출 코드는 <code>next/yield</code>를 이용해 결과를 전달 및 교환합니다.</p>
<p>예시 : </p>
<pre><code class="language-javascript">function* gen() {
  let ask1 = yield &quot;2 + 2 = ?&quot;;

  console.log(ask1); // 4

  let ask2 = yield &quot;3 * 3 = ?&quot;;

  console.log(ask2); // 9
}

let generator = gen();

console.log( generator.next().value ); // &quot;2 + 2 = ?&quot;

console.log( generator.next(4).value ); // &quot;3 + 3 = ?&quot;

console.log( generator.next(9).done ); // true</code></pre>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/88eda2f7-7abc-48cf-8952-016b2f538635/image.png" alt=""></p>
<p>실행 흐름을 나타낸 그림은 다음과 같습니다.</p>
<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/80c99e73-3b4b-4ada-926b-56296477ef4a/image.png" alt=""></p>
<ol>
<li><p>제너레이터 객체가 만들어지고 첫 번째 <code>.next()</code>가 호출되면, 그 실행이 시작되고 첫 번째 <code>yield</code>에 다다릅니다.</p>
</li>
<li><p>산출 값은 바깥 코드로 반환됩니다.</p>
</li>
<li><p>두 번째 <code>.next(4)</code>는 첫 번째 <code>yield</code>의 결과가 될 <code>4</code>를 제너레이터 안으로 전달합니다. 그리고 다시 실행이 이어집니다.</p>
</li>
<li><p>실행 흐름이 두 번째 <code>yield</code>에 다다르고, 산출값(<code>&quot;3 * 3 = ?&quot;</code>)이 제너레이터 호출 결과가 됩니다.</p>
</li>
<li><p>세 번째 <code>next(9)</code>는 두 번째 <code>yield</code>의 결과가 될 <code>9</code>를 제너레이터 안으로 전달합니다. 그리고 다시 실행이 이어지는데, <code>done: true</code>이므로 제너레이터 함수는 종료됩니다.</p>
</li>
</ol>
<p>첫 번째 <code>next()</code>를 제외한 각 <code>next(value)</code>는 현재 <code>yield</code>의 결과가 될 값을 제너레이터 안에 전달합니다. 그리고 다음 <code>yield</code>의 결과로 되돌아옵니다.</p>
<hr>
<h1 id="generatorthrow">generator.throw</h1>
<p><code>yield</code>의 결과가 될 값을 제너레이터 안에 전달하기도 합니다.</p>
<p>그런데 외부 코드가 에러를 만들거나 던질 수 있습니다. 에러는 결과의 한 종류이기 때문에 이는 자연스러운 현상입니다.</p>
<p>에러를 <code>yield</code> 안으로 전달하려면 <code>generator.throw(err)</code>를 호출해야 합니다. <code>generator.throw(err)</code>를 호출하게 되면 <code>err</code>는 <code>yield</code>가 있는 줄로 던져집니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[공백으로 구분하기 - Array.split(정규표현식)]]></title>
            <link>https://velog.io/@nawhes_joo/%EA%B3%B5%EB%B0%B1%EC%9C%BC%EB%A1%9C-%EA%B5%AC%EB%B6%84%ED%95%98%EA%B8%B0-Array.split%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D</link>
            <guid>https://velog.io/@nawhes_joo/%EA%B3%B5%EB%B0%B1%EC%9C%BC%EB%A1%9C-%EA%B5%AC%EB%B6%84%ED%95%98%EA%B8%B0-Array.split%EC%A0%95%EA%B7%9C%ED%91%9C%ED%98%84%EC%8B%9D</guid>
            <pubDate>Wed, 28 Feb 2024 02:52:38 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/21971ce9-d972-407a-8046-80a0b06e9fca/image.png" alt=""></p>
<p>Array.split(/\s+/)으로 공백을 기준으로 나누고
filter(word =&gt; word)로 빈 값을 제거하였다.</p>
<p>filter의 리턴값은 boolean이다.
true인 값만 남겨둔다.</p>
<p>word =&gt; word는 빈 문자열이 아닌 경우에 true를 반환한다.</p>
<hr>
<blockquote>
<ul>
<li>정규표현식</li>
</ul>
</blockquote>
<pre><code>-- 종류 

^: 문자열의 시작 
$: 문자열의 끝 
[]: 대괄호 안의 어떤 문자든 하나와 일치
|: OR 조건
.: 어떤 문자든 하나와 일치
*: 0회 이상 반복
+: 1회 이상 반복
?: 0 또는 1회의 발생
\: 이스케이프 문자로, 특수 문자의 원래 의미를 상실시키고 문자 자체와 매칭


-- 예제

/^abc/: &quot;abc&quot;로 시작하는 문자열에 일치
/[0-9]+/: 하나 이상의 숫자에 일치
/\.png$/: &quot;.png&quot;로 끝나는 문자열에 일치
/[aeiou]/: 모음 중 하나에 일치
/^\d{3}-\d{2}-\d{4}$/: &quot;123-45-6789&quot; 형식의 미국 소셜 보안 번호에 일치
/\d/g : 전역에서 숫자만</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[5명씩 - Array.reduce()]]></title>
            <link>https://velog.io/@nawhes_joo/5%EB%AA%85%EC%94%A9-array.reduce</link>
            <guid>https://velog.io/@nawhes_joo/5%EB%AA%85%EC%94%A9-array.reduce</guid>
            <pubDate>Wed, 28 Feb 2024 02:02:32 GMT</pubDate>
            <description><![CDATA[<p><img src="https://velog.velcdn.com/images/nawhes_joo/post/fe58898f-ab66-4b5f-996b-77a260b9b12d/image.png" alt=""></p>
<pre><code class="language-javascript">function solution(names) {
    return names.reduce((acc, cur, idx) =&gt; {
        if(idx % 5 === 0) acc.push(cur);
        return acc;
    }, []); // , []는 acc를 배열로 초기화한다는 의미이다.
}</code></pre>
<blockquote>
<ul>
<li>reduce의 인자</li>
</ul>
</blockquote>
<ol>
<li>accumulator - 누적된 값(누적기)</li>
<li>currentValue - 현재 배열에서 처리 중인 요소</li>
<li>currentIndex - 현재 배열에서 처리 중인 요소의 인덱스</li>
<li>array - &#39;reduce&#39;가 호출된 배열</li>
</ol>
]]></description>
        </item>
    </channel>
</rss>