<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>모아이</title>
    <link>https://mo-i-programmers.tistory.com/</link>
    <description>개발 관련 블로그입니다</description>
    <language>ko</language>
    <pubDate>Sun, 31 May 2026 16:48:18 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>모_아이</managingEditor>
    <item>
      <title>docker로 mysql띄우고 Next.js로 연결해보기(prisma)-3</title>
      <link>https://mo-i-programmers.tistory.com/87</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;저번에 docker로 띄운 mysql을 next.js 프로젝트에 연결해서 테이블을 만드는 것까지 진행해 봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 간단하게 next.js에서 입력폼을 만들어보자&lt;br /&gt;컴포넌트 폴더를 따로 만들어서 제작했다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;컴포넌트 세팅&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 &lt;code&gt;app&lt;/code&gt;폴더 바로 아래 있는 &lt;code&gt;page.tsx&lt;/code&gt; 파일에 기본적으로 되어 있는 세팅들을 삭제하고 시작하자.&lt;br /&gt;&lt;code&gt;global.css&lt;/code&gt;에 있는 css 들도 다 삭제 했다. &lt;b&gt;tailwind를 사용할 예정이라 &lt;code&gt;@tailwind ~&lt;/code&gt; 부분은 삭제하지 말자&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// app/page.tsx


export default function Home() {

  return (
    &amp;lt;main className=&quot;min-h-screen flex flex-col justify-center items-center space-y-10&quot;&amp;gt;&amp;lt;/main&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다 삭제하고 입력 폼과 띄울 투두 리스트만 가운데로 오도록 세팅했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 투두를 만들 입력 폼과 데이터를 받을 &lt;code&gt;TodoForm.tsx&lt;/code&gt;과 &lt;code&gt;PostList.tsx&lt;/code&gt;를 만들어보자&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;components&lt;/code&gt;폴더는 어디에 만드나? 할 수 있는데 &lt;code&gt;app&lt;/code&gt;디렉토리 안에 만들어도 되고 app외부로 나와서 루트에서 만들든 상관은 없다.&lt;br /&gt;다만 주의할 점으론 &lt;code&gt;app&lt;/code&gt; 폴더에 만들경우 현재 Next.js는 라우팅을 자동으로 해주는데 우리가 만든 &lt;code&gt;components&lt;/code&gt;폴더 또한 라우팅 될 수 있다(물론 &lt;code&gt;page.tsx&lt;/code&gt;를 만들지 않을 경우 엑세스 하진 않는다)&lt;br /&gt;그래서 &lt;code&gt;app&lt;/code&gt;폴더 안에 &lt;code&gt;components&lt;/code&gt;폴더를 만들고 싶은 경우 앞에 &lt;code&gt;_&lt;/code&gt;를 붙여서 라우팅 되지 않도록 만들어주자&lt;br /&gt;&lt;code&gt;_components&lt;/code&gt;라고 폴더를 만들어줄 경우 Next.js에서 이를 인식하고 라우팅을 하지 않는다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;// components/TodoForm.tsx

export default function TodoForm() {
  return (
    &amp;lt;form
      className=&quot;flex flex-col justify-center items-center rounded shadow-md  bg-sky-200 p-5 space-y-3&quot;
    &amp;gt;
      &amp;lt;input type=&quot;text&quot; name=&quot;title&quot; className=&quot;p-2 rounded&quot; /&amp;gt;
      &amp;lt;input type=&quot;text&quot; name=&quot;content&quot; className=&quot;p-2 rounded&quot; /&amp;gt;
      &amp;lt;button className=&quot;p-2 bg-slate-300 w-24 rounded &quot;&amp;gt;submit&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 모양으로 만들었고 만들어진 모양을 보면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;922&quot; data-origin-height=&quot;677&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bl53EV/btsHF3gAMqZ/o30IGVp9dUSbT47SBimNGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bl53EV/btsHF3gAMqZ/o30IGVp9dUSbT47SBimNGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bl53EV/btsHF3gAMqZ/o30IGVp9dUSbT47SBimNGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbl53EV%2FbtsHF3gAMqZ%2Fo30IGVp9dUSbT47SBimNGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;538&quot; height=&quot;395&quot; data-origin-width=&quot;922&quot; data-origin-height=&quot;677&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤 효과도 입히지 않은 정말 간단한 form 입력창이 하나 만들어졌고 다음으로 &lt;code&gt;PostList&lt;/code&gt;또한 간단하게 만들어보자&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// components/PostList.tsx

export default function PostList() {
  return (
    &amp;lt;div className=&quot;w-3/5 flex flex-col items-center space-y-5&quot;&amp;gt;
      &amp;lt;div className=&quot;p-5 bg-slate-100 w-full rounded shadow-lg flex flex-col space-y-2 &quot;&amp;gt;
        &amp;lt;h1 className=&quot;font-bold text-xl&quot;&amp;gt;Title : title부분&amp;lt;/h1&amp;gt;
        &amp;lt;p&amp;gt;내용 부분&amp;lt;/p&amp;gt;
        &amp;lt;span className=&quot;text-slate-500 text-sm&quot;&amp;gt;Create : 만든날짜&amp;lt;/span&amp;gt;
      &amp;lt;/div&amp;gt;
      &amp;lt;div className=&quot;p-5 bg-slate-100 w-full rounded shadow-lg flex flex-col space-y-2 &quot;&amp;gt;
        &amp;lt;h1 className=&quot;font-bold text-xl&quot;&amp;gt;Title : title부분&amp;lt;/h1&amp;gt;
        &amp;lt;p&amp;gt;내용 부분&amp;lt;/p&amp;gt;
        &amp;lt;span className=&quot;text-slate-500 text-sm&quot;&amp;gt;Create : 만든날짜&amp;lt;/span&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단은 목록이기에 2개를 붙였고 list가 불러질 땐 하나로 돌릴 예정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 완성된 화면을 확인하면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1327&quot; data-origin-height=&quot;1187&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/belKFH/btsHHijuZNP/WuxlGetkb2KcaMB5Bx7KI1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/belKFH/btsHHijuZNP/WuxlGetkb2KcaMB5Bx7KI1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/belKFH/btsHHijuZNP/WuxlGetkb2KcaMB5Bx7KI1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbelKFH%2FbtsHHijuZNP%2FWuxlGetkb2KcaMB5Bx7KI1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;492&quot; height=&quot;440&quot; data-origin-width=&quot;1327&quot; data-origin-height=&quot;1187&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 아주 간단한 입력창과 todo 창이 완성된 걸 확인해 볼 수 있다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Server&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이제 Next.js 의 server action을 통해 prisma에 접근해야 하는데 이는 공식문서를 참고하자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations&quot;&gt;https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions-and-mutations&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;LIST불러오기(getTodos)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 Next.js를 사용하며 &lt;code&gt;&quot;use client&quot;&lt;/code&gt;를 통해 클라이언트 컴포넌트를 나누듯이 &lt;code&gt;&quot;use server&quot;&lt;/code&gt;를 통해 서버에서 동작하도록 만들 수 있다.&lt;br /&gt;각 함수 내부에서 사용해도 되지만 나는 루트 폴더에 &lt;code&gt;server&lt;/code&gt;폴더를 만들어 server에 관련된 부분들을 해당 폴더에 넣을 예정이다&lt;br /&gt;&lt;code&gt;server&lt;/code&gt;폴더 안에 &lt;code&gt;actions&lt;/code&gt;폴더를 만들어 &lt;code&gt;post.ts&lt;/code&gt;란 파일을 만들어 투두리스트를 불러오는 함수와 추가하는 함수를 구현해보자&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 전에 이전에 만들었던 &lt;code&gt;/lib/prisma.ts&lt;/code&gt;에 작성한 쿼리 client 부분을 지우고 새로 &lt;code&gt;clinet&lt;/code&gt;라는 폴더를 만들어서 &lt;code&gt;clinet.ts&lt;/code&gt;란 파일에 새롭게 적자.&lt;br /&gt;이전에 &lt;code&gt;production&lt;/code&gt; 상태등을 고려해서 작성했고 정상적으로 작동하지만 typescript가 제대로 동작하지 않는 경우가 있어 일단 수정했다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// @client/client.ts
import { PrismaClient } from &quot;@prisma/client&quot;;

export const prisma = new PrismaClient();&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹시 &lt;code&gt;import&lt;/code&gt;가 안된다면 &lt;code&gt;npm install @prisma/client&lt;/code&gt;로 직접 설치해주면 해결된다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이를 이용해 todolist를 불러오는 server action 함수를 만들어보자&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;//@server/actions/post.ts

&quot;use server&quot;;

import { prisma } from &quot;@/client/client&quot;;

export const getTodos = async () =&amp;gt; {
  const todos = await prisma.post.findMany();

  return todos;
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;prisma.post.findMany()&lt;/code&gt;가 무엇인가? 궁금한데.. prisma는 우리가 이전 &lt;code&gt;client/client.ts&lt;/code&gt;에서 만들었던 &lt;code&gt;new PrismaClient&lt;/code&gt; 객체로 우리가 schema로 만들었던 모델들이 모두 포함 되어 있다&lt;br /&gt;그리고 자체적으로 주어지는 쿼리들이 존재하며 우리가 만들었던 &lt;code&gt;post&lt;/code&gt;로 접근할시 prisma에서 만들어놓은 많은 데이터 쿼리들이 존재하는데 상황에 맞춰 공식문서를 찾아보며 사용해보면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.prisma.io/docs/orm/prisma-client/queries&quot;&gt;https://www.prisma.io/docs/orm/prisma-client/queries&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 이곳에서 전부 찾아와주는 메서드인 &lt;code&gt;findMany()&lt;/code&gt;를 사용했다. 이는 &lt;code&gt;SELECT * FROM post&lt;/code&gt;와 동일하게 동작한다.&lt;br /&gt;그리고 docker에 올라가 있는 mysql에서 받은 리스트들을 &lt;code&gt;return&lt;/code&gt; 해주고 이를 화면에 뿌려주면 된다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// @components/PostList.tsx

import { getTodos } from &quot;@/server/actions/post&quot;;

export default async function PostList() {
  const todos = await getTodos();

  return (
    &amp;lt;div className=&quot;w-3/5 flex flex-col items-center space-y-5&quot;&amp;gt;
      {todos.map(({ id, title, content, createdAt }) =&amp;gt; (
        &amp;lt;div
          key={id}
          className=&quot;p-5 bg-slate-100 w-full rounded shadow-lg flex flex-col space-y-2 &quot;
        &amp;gt;
          &amp;lt;h1 className=&quot;font-bold text-xl&quot;&amp;gt;Title : {title}&amp;lt;/h1&amp;gt;
          &amp;lt;p&amp;gt;{content}&amp;lt;/p&amp;gt;
          &amp;lt;span className=&quot;text-slate-500 text-sm&quot;&amp;gt;
            Create : {createdAt.toLocaleString()}
          &amp;lt;/span&amp;gt;
        &amp;lt;/div&amp;gt;
      ))}
    &amp;lt;/div&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드 부분은 끝났고 mysql에 대강 todolist 몇개를 강제로 &lt;code&gt;insert&lt;/code&gt;해서 띄워보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL Workbench에 들어가서 입력 시키고 db를 확인해보니 잘 들어간 걸 확인할 수 있다&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;insert into Post (title,content)
values('test2','Todo테스트2입니다');

insert into Post (title,content)
values('test3','Todo테스트3입니다');

-- ....some code

select * from Post;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1590&quot; data-origin-height=&quot;448&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/eC1mef/btsHHIoHILN/0rdBAAJxzHFkGKA5tMof2K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/eC1mef/btsHHIoHILN/0rdBAAJxzHFkGKA5tMof2K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/eC1mef/btsHHIoHILN/0rdBAAJxzHFkGKA5tMof2K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeC1mef%2FbtsHHIoHILN%2F0rdBAAJxzHFkGKA5tMof2K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;750&quot; height=&quot;211&quot; data-origin-width=&quot;1590&quot; data-origin-height=&quot;448&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 들어간 걸 확인했으니 그럼 화면에도 정상적으로 뜨는지 확인해보자.&lt;br /&gt;우리가 만든 컴포넌트들을 &lt;code&gt;page.tsx&lt;/code&gt;에도 잘 넣었다면 무사히 뜰것이다.&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import PostList from &quot;@/components/PostList&quot;;
import TodoForm from &quot;@/components/TodoForm&quot;;

export default function Home() {
  return (
    &amp;lt;main className=&quot;min-h-screen flex flex-col justify-center items-center space-y-10&quot;&amp;gt;
      &amp;lt;TodoForm /&amp;gt;
      &amp;lt;PostList /&amp;gt;
    &amp;lt;/main&amp;gt;
  );
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2113&quot; data-origin-height=&quot;1417&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MXktw/btsHF3OJeuK/K7UKkxNnXiL7kgyddyvGDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MXktw/btsHF3OJeuK/K7UKkxNnXiL7kgyddyvGDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MXktw/btsHF3OJeuK/K7UKkxNnXiL7kgyddyvGDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMXktw%2FbtsHF3OJeuK%2FK7UKkxNnXiL7kgyddyvGDK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;648&quot; height=&quot;435&quot; data-origin-width=&quot;2113&quot; data-origin-height=&quot;1417&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드도 잘 들어갔고? 화면도 잘 뜬다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;굉장히 쉽게 완성됐고 이제 todo를 작성할 수만 있다면 완성이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Todo 추가하기(addTodo)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아까 작성하던 &lt;code&gt;@server/actions/post.ts&lt;/code&gt;로 돌아가서 &lt;code&gt;addTodo&lt;/code&gt; 함수를 완성해보자&lt;/p&gt;
&lt;pre class=&quot;dart&quot;&gt;&lt;code&gt;// @server/actions/post.ts
&quot;use server&quot;

// ...

export const addTodo = async (formData: FormData) =&amp;gt; {
  const title = formData.get(&quot;title&quot;);
  const content = formData.get(&quot;content&quot;);
  if (title instanceof File || content instanceof File || !title || !content)
    return console.error(&quot;Please enter the title and content&quot;);
  await prisma.post.create({ data: { title, content } });
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 form에 가서&lt;/p&gt;
&lt;pre class=&quot;sqf&quot;&gt;&lt;code&gt;// @components/TodoForm.tsx
import { addTodo } from &quot;@/server/actions/post&quot;;

export default function TodoForm() {
  return (
    &amp;lt;form
      action={addTodo}
      className=&quot;flex flex-col justify-center items-center rounded shadow-md  bg-sky-200 p-5 space-y-3&quot;
    &amp;gt;
      &amp;lt;input type=&quot;text&quot; name=&quot;title&quot; className=&quot; p-2 rounded&quot; /&amp;gt;
      &amp;lt;input type=&quot;text&quot; name=&quot;content&quot; className=&quot; p-2 rounded&quot; /&amp;gt;
      &amp;lt;button className=&quot;p-2 bg-slate-300 w-24 rounded &quot;&amp;gt;submit&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;form&lt;/code&gt;태그의 &lt;code&gt;action&lt;/code&gt; 부분에 우리가 작성한 &lt;code&gt;addTodo&lt;/code&gt;함수를 집어 넣으면 완성된다.&lt;br /&gt;&lt;code&gt;addTodo&lt;/code&gt; 함수를 잠깐 살펴보자면 이는 &lt;code&gt;form&lt;/code&gt;태그에서 &lt;code&gt;action&lt;/code&gt;을 사용한다면 매개변수 부분에서 &lt;code&gt;FormData&lt;/code&gt;가 들어오게 되고 이는 &lt;code&gt;get&lt;/code&gt;을 이용해서 데이터를 꺼내야 한다.&lt;br /&gt;&lt;b&gt;이때 데이터를 꺼낼때 이름은 각 &lt;code&gt;input&lt;/code&gt;의 &lt;code&gt;name&lt;/code&gt;값으로 정해지니 &lt;code&gt;name&lt;/code&gt;을 잘 맞춰주자&lt;/b&gt;&lt;br /&gt;데이터를 꺼낸 후 바로 타입을 살펴보면 &lt;code&gt;FormDataEntryValue | null&lt;/code&gt;라는 타입이 각각 &lt;code&gt;title&lt;/code&gt;과 &lt;code&gt;contetn&lt;/code&gt;에 붙어있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이를 바로 &lt;code&gt;prisma.post.create&lt;/code&gt;를 사용해 생성시킨다면 타입 관련한 에러가 나오게 되는데&lt;br /&gt;이는 우리가 &lt;code&gt;schema.prisma&lt;/code&gt;에서 선언한 타입들을 그대로 가져오기 때문에 &lt;code&gt;string&lt;/code&gt;과 충돌나는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 타입 네로잉 즉 타입을 &lt;code&gt;string&lt;/code&gt;으로 좁히기 위해 각각 &lt;code&gt;title&lt;/code&gt;과 &lt;code&gt;conetent&lt;/code&gt;는 &lt;code&gt;File&lt;/code&gt;타입이고 &lt;code&gt;null&lt;/code&gt;인지 체크해서 맞다면 함수를 더이상 진행하지 않고 &lt;code&gt;return&lt;/code&gt;하도록 했다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 무사히 &lt;code&gt;if&lt;/code&gt;문 쪽을 통과한다면 &lt;code&gt;title&lt;/code&gt;과 &lt;code&gt;content&lt;/code&gt;는 &lt;code&gt;string&lt;/code&gt;타입만 남게되고 이를 통해 우리가 설정한 타입 그대로 &lt;code&gt;prisma.post.create&lt;/code&gt;의 &lt;code&gt;data&lt;/code&gt;로 각각 넘겨줄 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행을 해보면&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;384&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AhlHI/btsHGy8IqTa/9tVwAeZNOCi4OFzLnQPfE1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AhlHI/btsHGy8IqTa/9tVwAeZNOCi4OFzLnQPfE1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AhlHI/btsHGy8IqTa/9tVwAeZNOCi4OFzLnQPfE1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAhlHI%2FbtsHGy8IqTa%2F9tVwAeZNOCi4OFzLnQPfE1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;374&quot; height=&quot;220&quot; data-origin-width=&quot;652&quot; data-origin-height=&quot;384&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 값들을 넣은다음 제출버튼을 누른 후 데이터가 db에 들어갔는지 워크벤치로 가서 확인해보자&lt;/p&gt;
&lt;pre id=&quot;code_1716972324035&quot; class=&quot;n1ql&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;select * from Post;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 쿼리를 적어 확인해보니&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1286&quot; data-origin-height=&quot;429&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/8f9md/btsHGggcEcR/EkbkNyKu2aIDMyFrt1VyQK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/8f9md/btsHGggcEcR/EkbkNyKu2aIDMyFrt1VyQK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/8f9md/btsHGggcEcR/EkbkNyKu2aIDMyFrt1VyQK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F8f9md%2FbtsHGggcEcR%2FEkbkNyKu2aIDMyFrt1VyQK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;629&quot; height=&quot;210&quot; data-origin-width=&quot;1286&quot; data-origin-height=&quot;429&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무사히 db에 도착한 것 같다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면도 새로고침을 해보니&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2184&quot; data-origin-height=&quot;1506&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dtOYOk/btsHHO3CUVV/K4uCoRkdXRfN2Lbzgf3mGk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dtOYOk/btsHHO3CUVV/K4uCoRkdXRfN2Lbzgf3mGk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dtOYOk/btsHHO3CUVV/K4uCoRkdXRfN2Lbzgf3mGk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdtOYOk%2FbtsHHO3CUVV%2FK4uCoRkdXRfN2Lbzgf3mGk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;664&quot; height=&quot;458&quot; data-origin-width=&quot;2184&quot; data-origin-height=&quot;1506&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;변경된 데이터를 받아 띄워주는 걸 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금 페이지들은 서버 컴포넌트들로 즉각적인 변화가 일어나지 않기 때문에 새로고침을 해줘야 하는데 이부분은 Next.js를 공부하면서 더 수정해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 글의 목표인 Next.js 기반으로 Prisma, ServerAction, docker, My SQL을 활용해서 로컬에서 돌아갈 간단한 웹 페이지와 서버, db를 연결시키는 테스트를 해봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가적으로 어떤 걸 더 해볼지는 개인 적으로 하나씩 해보면 좋을 것 같은데 나는 tRPC를 활용해서 server action과 함께 묶어보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>업무</category>
      <author>모_아이</author>
      <guid isPermaLink="true">https://mo-i-programmers.tistory.com/87</guid>
      <comments>https://mo-i-programmers.tistory.com/87#entry87comment</comments>
      <pubDate>Wed, 29 May 2024 17:49:07 +0900</pubDate>
    </item>
    <item>
      <title>docker로 mysql띄우고 Next.js로 연결해보기(prisma)-2</title>
      <link>https://mo-i-programmers.tistory.com/86</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이전 글에서 &lt;code&gt;docker&lt;/code&gt;를 이용해 &lt;code&gt;mysql&lt;/code&gt;을 띄웠고 이후 workbench를 이용해 데이터 베이스에 쉽게 접속할 수 있도록 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이제 이를 실제로 코드상에서 사용해보려 하는데 &lt;code&gt;Next.js&lt;/code&gt;와 &lt;code&gt;Prisma&lt;/code&gt;를 이용해서 만들어보려고 한다.&lt;br /&gt;이를 통해 &lt;code&gt;prisma&lt;/code&gt;의 사용법을 간단하게 보고 특히 &lt;code&gt;Next.js&lt;/code&gt;의 Server action을 잘 익혀볼 수 있을 것 같다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;프로젝트 설정&lt;/h1&gt;
&lt;pre class=&quot;autoit&quot;&gt;&lt;code&gt;npx create-next-app@latest&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 통해 간단한 &lt;code&gt;next-app&lt;/code&gt;을 만든다. 이름은 &lt;code&gt;sql_test&lt;/code&gt;로 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성공적으로 &lt;code&gt;sql_test&lt;/code&gt;가 설치 되었다면 추가적으로 &lt;code&gt;prisma&lt;/code&gt;를 설치하자&lt;/p&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;cd sql_test
npm i prisma --save-dev
npx prisma init --datasource-provider mysql&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;혹시 &lt;code&gt;sql_test&lt;/code&gt; 폴더가 아닌 바깥쪽에서 설치할 까 &lt;code&gt;cd sql_test&lt;/code&gt;도 넣었다.&lt;br /&gt;무사히 잘 설치 했다면 &lt;code&gt;sql_test&lt;/code&gt;폴더 안에 &lt;code&gt;prisma&lt;/code&gt;라는 폴더가 생긴 걸 확인할 수 있다&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 주의 할 점으로 만약 &lt;code&gt;git&lt;/code&gt;으로 해당 프로젝트를 올릴 생각을 하고 있다면 방금 &lt;code&gt;prisma&lt;/code&gt;와 함께 생긴 &lt;code&gt;.env&lt;/code&gt;파일을 되도록 git remote 에는 올리지 말아야 하는데 &lt;code&gt;.gitignore&lt;/code&gt;에 들어가서 &lt;code&gt;.env&lt;/code&gt;를 추가하자.&lt;br /&gt;그럼 git의 추적이 끊어진 걸 볼 수 있다&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;prisma&lt;/code&gt; 폴더 안에는 &lt;code&gt;schema.prisma&lt;/code&gt;라는 파일이 하나 같이 생성되어 있는데 내부를 살펴보면&lt;/p&gt;
&lt;pre class=&quot;dts&quot;&gt;&lt;code&gt;// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init

generator client {
  provider = &quot;prisma-client-js&quot;
}

datasource db {
  provider = &quot;mysql&quot;
  url      = env(&quot;DATABASE_URL&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 만들어져 있다. Prisma클라이언트를 생성하고 아래는 db를 연결하는데 &lt;code&gt;mysql&lt;/code&gt;이며 &lt;code&gt;url&lt;/code&gt;부분은 &lt;code&gt;env()&lt;/code&gt;를 통해 &lt;code&gt;.env&lt;/code&gt;파일에 접근 하여 &lt;code&gt;DATABASE_URL&lt;/code&gt;을 가져온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 &lt;code&gt;.env&lt;/code&gt;와 &lt;code&gt;DATABASE_URL&lt;/code&gt;을 만든적이 없는데 어떻게 된걸까? 프로젝트 루트 폴더에서 잘 살펴보면 &lt;code&gt;.env&lt;/code&gt;파일이 만들어져 있고 내부를 살펴보면&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema

# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings

DATABASE_URL=&quot;mysql://johndoe:randompassword@localhost:3306/mydb&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 존재하는 걸 확인할 수 있다.&lt;br /&gt;url의 형식은 내가 사용하는 데이터베이스마다 형식이 다르니 각자 참고해서 넣어보자 &lt;code&gt;MySQL&lt;/code&gt;같은 경우는 아래와 같다.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;mysql://USER:PASSWORD@HOST:PORT/DATABASE&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;임으로&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;USER&lt;/code&gt; : 데이터베이스 사용자의 이름&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PASSWORD&lt;/code&gt; : 데이터베이스 사용자의 비밀번호&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PORT&lt;/code&gt; : 데이터베이스 서버가 실행중인 포트(일반적으로 MySQL의 경우 &lt;code&gt;3306&lt;/code&gt;이다)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DATABASE&lt;/code&gt; : 데이터베이스 이름&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리는 저번에 만든 걸 넣어본다면&lt;/p&gt;
&lt;pre class=&quot;ini&quot;&gt;&lt;code&gt;DATABASE_URL=&quot;mysql://dev:1234@localhost:3306/test&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러면 Prisma가 데이터 베이스에 접근할 수 있도록 성공적으로 작성했다. 이따가 테스트를 통해 잘 들어가는지 확인을 해보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 넣고 싶은 건 간단하게 제목과 내용을 가진 글목록이고 이를 테이블로 만들어 보고 싶다.&lt;/p&gt;
&lt;pre class=&quot;puppet&quot;&gt;&lt;code&gt;
generator client {
  provider = &quot;prisma-client-js&quot;
}

datasource db {
  provider = &quot;mysql&quot;
  url      = env(&quot;DATABASE_URL&quot;)
}

model Post {
  id Int @id @default(autoincrement())
  createdAt DateTime @default(now())
  title String @db.VarChar(255)
  content String?
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Post&lt;/code&gt;라는 테이블에 다음과 같은 컬럼을 만든다&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;id&lt;/code&gt; : &lt;code&gt;Int&lt;/code&gt;타입이며 &lt;code&gt;@id&lt;/code&gt;역할을 하고 &lt;code&gt;@default&lt;/code&gt;기본적으로 &lt;code&gt;autoincrement&lt;/code&gt;자동 증가하게 해준다. 즉, 자동적으로 증가하는 아이디가 붙을거란 것&lt;/li&gt;
&lt;li&gt;&lt;code&gt;createdAt&lt;/code&gt; : 생성 날짜인데 &lt;code&gt;DateTime&lt;/code&gt; 타입이며 기본적으로 &lt;code&gt;now&lt;/code&gt;현재 시간으로 넣을것이다&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등 아래 &lt;code&gt;title&lt;/code&gt;과 &lt;code&gt;content&lt;/code&gt;는 찾아보며 유추 가능할 것이라 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 prisma를 작성하고 다시 명령어 창으로 돌아와 아래의 명령어를 입력해주면?&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;npx prisma migrate dev --name init&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 작성한 prisma 모델이 올라가게 되고 &lt;code&gt;migration&lt;/code&gt;기록이 db에 함께 저장되게 된다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리 프로젝트에도 새로운 폴더와 파일이 생성되는데&lt;/p&gt;
&lt;pre id=&quot;code_1715587748137&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;prisma/migrations/2024...(마이그레이션시간)/migration.sql&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 만들어지게 된다. 이 내부를 확인해보면&lt;/p&gt;
&lt;pre id=&quot;code_1715587774610&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE TABLE `Post` (
    `id` INTEGER NOT NULL AUTO_INCREMENT,
    `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
    `title` VARCHAR(255) NOT NULL,
    `content` VARCHAR(191) NULL,

    PRIMARY KEY (`id`)
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;'이처럼 되어 있는데 우리가 아까 prisma 파일에서 선언한 model을 prisma가 자동적으로 sql문으로 변경하여 데이터 베이스 서버에 날려주는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고로 데이터 베이스를 확인하러 workbench로 들어가보면?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;971&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbHWaz/btsHnpR5NMm/BD74V20AwWJOWztXsvA6k0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbHWaz/btsHnpR5NMm/BD74V20AwWJOWztXsvA6k0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbHWaz/btsHnpR5NMm/BD74V20AwWJOWztXsvA6k0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbHWaz%2FbtsHnpR5NMm%2FBD74V20AwWJOWztXsvA6k0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1500&quot; height=&quot;971&quot; data-origin-width=&quot;1500&quot; data-origin-height=&quot;971&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 설정한 model이 다 그대로 들어간 걸 확인할 수 있다. 여기까지 오면 굉장히 신기하고 또 재밋게 느껴졌다.&lt;br /&gt;그럼 이 다음에는 클라이언트 쪽에서 무언가를 할 수 있어야 하는데..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;여기서&lt;br /&gt;공식문서에 따르면 &lt;code&gt;prisma@client&lt;/code&gt;를 새로 설치하라고 나오는데 설치하지 않고도 사용할 수 있었다. 아마 자동 설치되는 부분이 있는 것 같다.&lt;br /&gt;그러면 아래의 설치를 따라하지 않고 한번 진행해보자&lt;/b&gt;&lt;/p&gt;
&lt;pre class=&quot;coffeescript&quot;&gt;&lt;code&gt;npm install @prisma/client&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;데이터 베이스 쿼리&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 베이스에서 데이터를 읽고 쓰기 위한 쿼리 작성을 시작할 수 있는데&lt;br /&gt;일단 prisma 의 인스턴스를 생성해 줘야 한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;lib/prisma.ts&lt;/code&gt;라는 파일을 만들어 아래의 코드를 추가해주자&lt;/p&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;// lib/prisma.ts
import { PrismaClient } from &quot;@prisma/client&quot;;
declare global {
  let prisma: PrismaClient | undefined;
}
const client = prisma || new PrismaClient();
if (process.env.NODE_ENV !== &quot;production&quot;) prisma = client;
export default client;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 &lt;code&gt;prisma@client&lt;/code&gt;를 따로 &lt;code&gt;import&lt;/code&gt; 하지 않고 전역적으로 사용하기 위해 작성된 코드로 &lt;code&gt;declare global&lt;/code&gt;을 통해 글로벌 변수속 &lt;code&gt;prisma&lt;/code&gt;를 선언, 이후 &lt;code&gt;client&lt;/code&gt;를 만들어 만약 전역 변수 &lt;code&gt;prisma&lt;/code&gt;가 없다면 새로운 &lt;code&gt;prisma@client&lt;/code&gt;를 가지도록 하였고 아니라면 기존 글로벌 변수에 있는 &lt;code&gt;prisma&lt;/code&gt;를 사용하도록 한다.&lt;br /&gt;이때 만약 노드의 상태가 프로덕션이면 &lt;code&gt;client&lt;/code&gt;는 &lt;code&gt;prisma&lt;/code&gt; 전역변수에 할당하여 동일화 시킨 후 &lt;code&gt;client&lt;/code&gt;는 &lt;code&gt;export&lt;/code&gt;하여 전역적으로 사용할 수 있게 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 통해 다른 곳에서 &lt;code&gt;client&lt;/code&gt;를 통해 &lt;code&gt;prisma&lt;/code&gt;가 제공하는 데이터베이스 쿼리를 사용할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 다음 글에서 Next.js의 server action과 더불어 함께 다뤄보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Reference : &lt;a href=&quot;https://www.prisma.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.prisma.io/&lt;/a&gt; , &lt;a href=&quot;https://www.yujiseok.blog/post/nextjs-server-actions&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.yujiseok.blog/post/nextjs-server-actions&lt;/a&gt;&lt;/p&gt;</description>
      <category>업무</category>
      <category>docker</category>
      <category>MySQL</category>
      <category>Next.js</category>
      <category>Prisma</category>
      <author>모_아이</author>
      <guid isPermaLink="true">https://mo-i-programmers.tistory.com/86</guid>
      <comments>https://mo-i-programmers.tistory.com/86#entry86comment</comments>
      <pubDate>Mon, 13 May 2024 17:11:44 +0900</pubDate>
    </item>
    <item>
      <title>docker로 mysql띄우고 Next.js로 연결해보기(prisma)-1</title>
      <link>https://mo-i-programmers.tistory.com/85</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;제목만 보면 뭔가 되게 많아 보이는데 실은 별거 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker를 이용해서 가상 컨테이너로 mysql 서버를 만들고 Next.js와 prisma를 사용해서 쉽게 mysql에 내가 원하는 타입과 결과를 넣어보려고 하는거다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker와 next.js의 server action, prisma를 연습하기 위해 한번 실행해 봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;본 작성글은 window 환경에서 작업한 작성물입니다.&lt;/b&gt;&lt;/p&gt;
&lt;h1&gt;docker로 mySQL 컨테이너 올리기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 &lt;code&gt;docker&lt;/code&gt;가 있어야 하지 않을까? 해당 링크로 가서 &lt;code&gt;docker&lt;/code&gt;를 먼저 설치하자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.docker.com/get-started/&quot;&gt;https://www.docker.com/get-started/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무사히 설치가 됐다면 &lt;code&gt;cmd&lt;/code&gt; 창을 열자 &lt;code&gt;git bash&lt;/code&gt;나 &lt;code&gt;window powershell&lt;/code&gt;도 괜찮습니다.&lt;br /&gt;저는 &lt;code&gt;window powershell&lt;/code&gt;에서 실행했습니다.&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;docker pull mysql:8.0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 입력해주자. 하나씩 살펴보면 &lt;code&gt;docker&lt;/code&gt;는 &lt;code&gt;docker&lt;/code&gt;명령어들을 실행하기 위한 것이고 우리가 라이브러리 설치하려 할 때 앞에 &lt;code&gt;npm&lt;/code&gt;을 붙여 &lt;code&gt;npm&lt;/code&gt;을 사용하는 것과 같다.&lt;br /&gt;&lt;code&gt;pull&lt;/code&gt;은 다운로드 받는 걸 뜯하고 &lt;code&gt;mysql:8.0&lt;/code&gt;은 어떤걸 다운 받은 건가 이다.&lt;br /&gt;&lt;code&gt;npm install mysql:8.0&lt;/code&gt; 과 똑같이 생각하자.&lt;br /&gt;&lt;b&gt;이때 &lt;code&gt;mysql&lt;/code&gt;만 해도 되는데 뒤에 &lt;code&gt;:8.0&lt;/code&gt;은 뭐지? 할 수 있는데 이건 &lt;code&gt;mysql workbench&lt;/code&gt;를 사용하는데 있어 버전을 맞추기 위해 뒤에 버전을 따로 &lt;code&gt;tag&lt;/code&gt;한 것이다.&lt;br /&gt;이는 이따 다른 명령어들을 실행할 때도 주의해서 작성해주자&lt;/b&gt;&lt;br /&gt;무사히 &lt;code&gt;mysql:8.0&lt;/code&gt;이 설치 되었다면 한번 확인해보자&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;docker images&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 입력시 내가 다운 받은 이미지들을 전부 띄우는 것으로 입력히 아까 다운받은 &lt;code&gt;mysql&lt;/code&gt;이 떠야한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1113&quot; data-origin-height=&quot;64&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuBPPt/btsHpbrcXxr/2mSKc5hZf9Q4pkbF5v0cz0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuBPPt/btsHpbrcXxr/2mSKc5hZf9Q4pkbF5v0cz0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuBPPt/btsHpbrcXxr/2mSKc5hZf9Q4pkbF5v0cz0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuBPPt%2FbtsHpbrcXxr%2F2mSKc5hZf9Q4pkbF5v0cz0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1113&quot; height=&quot;64&quot; data-origin-width=&quot;1113&quot; data-origin-height=&quot;64&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위처럼&lt;code&gt;mysql&lt;/code&gt;이란 이름과 &lt;code&gt;tag&lt;/code&gt;부분에 &lt;code&gt;8.0&lt;/code&gt;으로 들어가 있으면 성공적으로 &lt;code&gt;mysql:8.0&lt;/code&gt;버전으로 이미지가 설치 된 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 컨테이너를 설정해서 올려야 한다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너와 이미지의 차이점은 docker 공식문서나 여러 블로그들을 참고해서 한번 알아보자.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=dev --name mysql_test mysql:8.0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;docker run&lt;/code&gt; :도커로 실행하겠다&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-d&lt;/code&gt; :&lt;code&gt;detach&lt;/code&gt;모드의 약자로 컨테이너가 백그라운드에서 실행된다 생각하면 된다. 우리가 어떤 다른 작업을 한다해도 docker container는 계속 올라가서 유지되고 있다는 것&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-p &amp;lt;호스트 포트&amp;gt; &amp;lt;컨테이너 포트&amp;gt; :&lt;/code&gt;p 3306:3306`으로 포트를 서로 연결한다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-e&lt;/code&gt; : 컨테이너에서 사용할 환경변수를 설정한다. 우리가 개발 할 때 &lt;code&gt;.env&lt;/code&gt;파일에 비밀 변수들을 추가하는 것이라 생각하면 된다&lt;br /&gt;여기선 &lt;code&gt;MYSQL_ROOOT_PASSWORD&lt;/code&gt;라는 이름의 환경변수를 &lt;code&gt;dev&lt;/code&gt;로 저장했다.&lt;br /&gt;이는 이따 컨테이너에서 mySQL에 접속할 때 사용할 비번을 설정하는 것!&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--name &amp;lt;container name&amp;gt;&lt;/code&gt; 컨테이너의 이름을 설정한다. 우리가 만든 컨테이너는 &lt;code&gt;mysql_test&lt;/code&gt;이다&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mysql:8.0&lt;/code&gt; 컨테이너를 어떤 이미지를 사용해서 띄울건지로 우리는 mysql:8.0으로 컨테이너를 띄웠다&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 한번 궁금할텐데 port는 왜 3306이어야 할까 한다.&lt;br /&gt;&lt;a href=&quot;https://www.cbtnuggets.com/common-ports/what-is-port-3306&quot;&gt;https://www.cbtnuggets.com/common-ports/what-is-port-3306&lt;/a&gt;&lt;br /&gt;이 페이지를 참고해보자!&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 컨테이너가 정상적으로 생성되고 돌아가고 있는지 확인하려면&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;docker ps -a&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 입력해서 확인 해 보자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2142&quot; data-origin-height=&quot;69&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPfBWI/btsHpcKsUeU/SmNsKgR8fA5ebEawazyFp0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPfBWI/btsHpcKsUeU/SmNsKgR8fA5ebEawazyFp0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPfBWI/btsHpcKsUeU/SmNsKgR8fA5ebEawazyFp0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPfBWI%2FbtsHpcKsUeU%2FSmNsKgR8fA5ebEawazyFp0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2142&quot; height=&quot;69&quot; data-origin-width=&quot;2142&quot; data-origin-height=&quot;69&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정상적으로 떠있는 걸 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 &lt;code&gt;STATUS&lt;/code&gt;부분을 확인해 보면 되고 &lt;code&gt;UP&lt;/code&gt;이면 컨테이너가 올라가 있다. 즉, 사용할 수 있다는 뜻이고 멈추고 싶다면 &lt;code&gt;docker container stop [컨테이너ID]&lt;/code&gt;를 입력해주자&lt;br /&gt;입력 후 다시 &lt;code&gt;docker ps -a&lt;/code&gt;를 작성해보면 방금과 동일하게 뜨지만 &lt;code&gt;STATUS&lt;/code&gt;부분이 &lt;code&gt;EXITED&lt;/code&gt;로 변경된 부분을 확인 할 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 docker를 올리고 싶다면 &lt;code&gt;run&lt;/code&gt;이나 &lt;code&gt;start&lt;/code&gt;를 쓰면 되는데 &lt;code&gt;run&lt;/code&gt;은 &lt;code&gt;attach&lt;/code&gt;모드가 디폴트이고 &lt;code&gt;start&lt;/code&gt;는 &lt;code&gt;detached&lt;/code&gt;모드가 디폴트이니 우리는 &lt;code&gt;detached&lt;/code&gt;모드를 부여했음으로 &lt;code&gt;start&lt;/code&gt;를 쓰자&lt;br /&gt;&lt;code&gt;docker start mysql_test&lt;/code&gt;를 입력해주고 다시 &lt;code&gt;docker ps -a&lt;/code&gt;를 입력하면 정상적으로 &lt;code&gt;STATUS&lt;/code&gt;가 &lt;code&gt;UP&lt;/code&gt;으로 바뀐 걸 볼 수 있다.&lt;/p&gt;
&lt;h1&gt;mySQL 컨테이너에 접속하여 설정하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무사히 컨테이너가 올라갔다면 이제 접속해서 권한 설정을 해보자&lt;/p&gt;
&lt;pre class=&quot;applescript&quot;&gt;&lt;code&gt;docker exec -it mysql_test bash&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 &lt;code&gt;docker exec&lt;/code&gt;가 컨테이너에 특정 명령을 실행할 수 있는 명령인데 이를 이용해서 컨테이너에 접속 할 수 있다.&lt;br /&gt;뒤에 &lt;code&gt;-it&lt;/code&gt;는 본래 &lt;code&gt;-i -t&lt;/code&gt; 의 명령어로 표준 입력 활성화와 컨테이너와 연결되지 않더라도 표준 입력을 유지한다. &lt;code&gt;TTY&lt;/code&gt;모드로 Bash를 사용하려면 해당 옵션을 사용하라고 되어 있다. 상호 입출력과 bash를 사용하기 위해 넣는다고 간단하게 일단 생각하고 넘어가자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 컨테이너 이름을 적어주고 뒤에 &lt;code&gt;bash&lt;/code&gt;를 사용해 &lt;code&gt;bash&lt;/code&gt;환경으로 실행하자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 이제 창에 명령 입력 부분이 이렇게 변할 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;359&quot; data-origin-height=&quot;39&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/mjKtG/btsHmZ0pIvX/p0fsaqB7jHQTN9bjNdz5M0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/mjKtG/btsHmZ0pIvX/p0fsaqB7jHQTN9bjNdz5M0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/mjKtG/btsHmZ0pIvX/p0fsaqB7jHQTN9bjNdz5M0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FmjKtG%2FbtsHmZ0pIvX%2Fp0fsaqB7jHQTN9bjNdz5M0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;359&quot; height=&quot;39&quot; data-origin-width=&quot;359&quot; data-origin-height=&quot;39&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 상태는 컨테이너에 접속하였고 bash 명령어들을 입력할 수 있게 되었다.&lt;br /&gt;그러면 mysql에 접속해보자&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;mysql -u root -p&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 입력시&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;494&quot; data-origin-height=&quot;61&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uStp5/btsHnJJk8AJ/QejKX7x2NlcBpoEEmUnEX0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uStp5/btsHnJJk8AJ/QejKX7x2NlcBpoEEmUnEX0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uStp5/btsHnJJk8AJ/QejKX7x2NlcBpoEEmUnEX0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuStp5%2FbtsHnJJk8AJ%2FQejKX7x2NlcBpoEEmUnEX0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;494&quot; height=&quot;61&quot; data-origin-width=&quot;494&quot; data-origin-height=&quot;61&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 비밀번호를 입력하라고 나오는데 우리가 위에서 환경변수로 만든 &lt;code&gt;MYSQL\_ROOT\_PASSWORD&lt;/code&gt; 에 입력한 값을 넣자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키보드로 입력하는데 화면에 아무것도 안나온다고 걱정하지 말자. 보안상 나타나지 않는 것이고 작성되고 있음으로 정확한 비밀번호를 입력하고 엔터로 입력시 무사히 mySQL에 접속 된 걸 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;mysql -u root -p&lt;/code&gt; 부분은 보통 우리가 &lt;code&gt;root&lt;/code&gt;의 경우 관리자 계정이고 관지라 계정으로 접속한다는 뜻으로 이해하자&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1286&quot; data-origin-height=&quot;406&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/l7hPz/btsHnhsYnvs/0mzIARBmVQ0SAUs9MPfwxk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/l7hPz/btsHnhsYnvs/0mzIARBmVQ0SAUs9MPfwxk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/l7hPz/btsHnhsYnvs/0mzIARBmVQ0SAUs9MPfwxk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fl7hPz%2FbtsHnhsYnvs%2F0mzIARBmVQ0SAUs9MPfwxk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1286&quot; height=&quot;406&quot; data-origin-width=&quot;1286&quot; data-origin-height=&quot;406&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 화면이 뜰 경우 성공적으로 접속했다.&lt;br /&gt;그럼 항상 관리자 모드로 접속 할 수는 없으니 우리가 사용할 유저 하나를 만들고 권한을 부여하자&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;CREATE USER 'dev'@'%' IDENTIFIED BY '1234';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;mysql&lt;/code&gt;문법을 사용해서 입력해주고 끝에 꼭 세미콜론(&lt;code&gt;;&lt;/code&gt;)은 잊지말고 작성해주자.&lt;code&gt;mysql&lt;/code&gt;의 기본임으로!&lt;br /&gt;&lt;code&gt;CREATE USER&lt;/code&gt; 유저를 생성하겠다 &lt;code&gt;'dev'&lt;/code&gt; dev란 이름으로 이때 &lt;code&gt;'%'&lt;/code&gt;외부에서 접근을 허용 하겠다.&lt;br /&gt;이후 뒤는 비밀번호 설정이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 권한을 부여한 &lt;code&gt;dev&lt;/code&gt;란 유저를 만들었다. 그럼 잘 만들어졌는지 확인해 보고 싶은데 어떻게 할까?&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;use mysql;
select host,user from user;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 입력시 &lt;code&gt;mysql&lt;/code&gt;이라는 기본적으로 &lt;code&gt;mysql&lt;/code&gt;이 가지고있는 데이터베이스로 접근할 수 있고 이후 &lt;code&gt;select&lt;/code&gt;문을 사용해서 user 부분을 가져올 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;578&quot; data-origin-height=&quot;429&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cwN611/btsHnW2R4Xn/xl0Ussm3lZnlUxm9ON7kx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cwN611/btsHnW2R4Xn/xl0Ussm3lZnlUxm9ON7kx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cwN611/btsHnW2R4Xn/xl0Ussm3lZnlUxm9ON7kx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcwN611%2FbtsHnW2R4Xn%2Fxl0Ussm3lZnlUxm9ON7kx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;578&quot; height=&quot;429&quot; data-origin-width=&quot;578&quot; data-origin-height=&quot;429&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 내가 만든 `dev`를 확인할 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 현재는 유저만 만든것이기에 따로 권한이 없어 mysql 작업을 못하니 이제 권한을 부여해보자&lt;br /&gt;다시 입력창에&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;GRANT ALL PRIVILEGES ON *.* TO 'dev'@'%';&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 명령어는 &lt;code&gt;dev&lt;/code&gt;에게 모든 권한을 부여했음을 뜻하며 권한을 따로따로 설정별로 주고 싶을 경우 mySQL문서에서 찾아서 권한을 따로 부여할 수 있도록 연습해보자.&lt;br /&gt;이후 우리는 꼭 &lt;code&gt;root&lt;/code&gt;가 아닌 &lt;code&gt;dev&lt;/code&gt;로 mysql에 접속할 수 있다.&lt;/p&gt;
&lt;pre class=&quot;cos&quot;&gt;&lt;code&gt;quit
mysql -u dev -p&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;quit&lt;/code&gt;를 통해 mysql에서 나온 뒤 다시 &lt;code&gt;mysql -u dev -p&lt;/code&gt;를 입력해서 내가 만든 유저로 무사히 접속 되는지 확인해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;만약 접속한 컨테이너 환경에서 나오고 싶을 경우 명령어 &lt;code&gt;exit&lt;/code&gt;를 입력시 나올 수 있다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 우리가 테스트 할 데이터 베이스를 하나만 만들고 컨테이너에서 나와보자&lt;br /&gt;&lt;code&gt;mysql -u dev -p&lt;/code&gt;로 다시 &lt;code&gt;mysql&lt;/code&gt;에 접속하고&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;CREATE DATABASE test;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 통해 &lt;code&gt;test&lt;/code&gt;라는 데이터 베이스를 만들고 잘 만들어 졌는지 확인해보자&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;show databases;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;449&quot; data-origin-height=&quot;359&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xJlmX/btsHnildfqP/niMY02WlMGMNQvjyEEWXW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xJlmX/btsHnildfqP/niMY02WlMGMNQvjyEEWXW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xJlmX/btsHnildfqP/niMY02WlMGMNQvjyEEWXW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxJlmX%2FbtsHnildfqP%2FniMY02WlMGMNQvjyEEWXW0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;449&quot; height=&quot;359&quot; data-origin-width=&quot;449&quot; data-origin-height=&quot;359&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 보면 &lt;code&gt;test&lt;/code&gt;라고 무사히 database가 만들어진 걸 확인해 볼 수 있다.&lt;br /&gt;그럼 아까 &lt;code&gt;mysql&lt;/code&gt;데이터 베이스에서 &lt;code&gt;user&lt;/code&gt;를 찾았던 것처럼 &lt;code&gt;test&lt;/code&gt;라는 데이터베이스에 접속하려면 어떻게 해야할까?&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code&gt;use test&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 &lt;code&gt;test&lt;/code&gt;데이터 베이스에 접속할 수 있다!&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이로써 docker 컨테이너를 이용해서 mysql 서버를 하나 만들어 보았다. 그럼 마지막으로 mysql workbench에만 한번 등록을 해보고 끝내보자&lt;/p&gt;
&lt;h1&gt;mySQL workbench에 등록하기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 mySQL workbench는 다운로드 하자. mySQL홈페이지로 들어가 설치 하면 된다.&lt;br /&gt;&lt;a href=&quot;https://dev.mysql.com/downloads/workbench/&quot;&gt;https://dev.mysql.com/downloads/workbench/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무사히 실행이 완료 되었다면 아래와 같은 화면일 텐데 My SQL Connections부분에 + 버튼을 눌러 데이터 베이스를 연결해보자&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1935&quot; data-origin-height=&quot;712&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjDuhI/btsHnWPnmFg/KsRMceDGhaFMLEfQBfkqx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjDuhI/btsHnWPnmFg/KsRMceDGhaFMLEfQBfkqx1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjDuhI/btsHnWPnmFg/KsRMceDGhaFMLEfQBfkqx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjDuhI%2FbtsHnWPnmFg%2FKsRMceDGhaFMLEfQBfkqx1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1935&quot; height=&quot;712&quot; data-origin-width=&quot;1935&quot; data-origin-height=&quot;712&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;+버튼을 클릭할 경우 아래와 같은 창이 나올텐데 순서대로 입력해주자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;connection name&lt;/code&gt;은 내가 만들었던 db이름으로 적어주고(본인이 우너하는 대로 적어도 상관없긴 하지만 맞춰서 적어줘야 직관적이다!)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1173&quot; data-origin-height=&quot;734&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9DBBx/btsHmEWsoW2/tOW2yGqJ0pct6h9K8PlYEK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9DBBx/btsHmEWsoW2/tOW2yGqJ0pct6h9K8PlYEK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9DBBx/btsHmEWsoW2/tOW2yGqJ0pct6h9K8PlYEK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9DBBx%2FbtsHmEWsoW2%2FtOW2yGqJ0pct6h9K8PlYEK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1173&quot; height=&quot;734&quot; data-origin-width=&quot;1173&quot; data-origin-height=&quot;734&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Hostname&lt;/code&gt;은 &lt;code&gt;docker&lt;/code&gt;로 올렸기 때문에 &lt;code&gt;localhost&lt;/code&gt;를 작성해주고 &lt;code&gt;port&lt;/code&gt;는 mysql 포트인 &lt;code&gt;3306&lt;/code&gt; 그대로 두고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;username&lt;/code&gt;은 아까 우리가 만든 user의 이름인 &lt;code&gt;dev&lt;/code&gt;를 집어 넣는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Default Schema&lt;/code&gt;는 우리가 기본으로 사용할 스키마를 고르는 것으로 아까 만든 &lt;code&gt;test&lt;/code&gt;라는 이름의 &lt;code&gt;database&lt;/code&gt;를 적어넣어서 기본적으로 &lt;code&gt;workbench&lt;/code&gt;에서 &lt;code&gt;mysql_test&lt;/code&gt;라는 데이터베이스에 접속시 &lt;code&gt;test&lt;/code&gt; 데이터베이스로 들어갈 수 있도록 한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 바로 들어가는게 아닌 &lt;code&gt;password&lt;/code&gt; 부분은 &lt;code&gt;Store in Vault&lt;/code&gt;를 통해 비밀번호를 입력하자!&lt;br /&gt;클릭할 시&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1056&quot; data-origin-height=&quot;600&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MBpbi/btsHowJyvFm/ik9FkLfCkUXx01JSN1kEAk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MBpbi/btsHowJyvFm/ik9FkLfCkUXx01JSN1kEAk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MBpbi/btsHowJyvFm/ik9FkLfCkUXx01JSN1kEAk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMBpbi%2FbtsHowJyvFm%2Fik9FkLfCkUXx01JSN1kEAk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1056&quot; height=&quot;600&quot; data-origin-width=&quot;1056&quot; data-origin-height=&quot;600&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 창이 나오는데 아까 우리가 아까 도커 컨테이너의 mysql에 들어가서 &lt;code&gt;CREATE USER&lt;/code&gt; 하면서 같이 적었던 비밀번호를 입력해주자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;OK&lt;/code&gt;를 누른 뒤 우측 하단의 &lt;code&gt;Test Connection&lt;/code&gt;으로 정상적으로 연결 되었는지 확인해보자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;Successfully&lt;/code&gt;로 뜰경우 성공적으로 연결된 것이니 &lt;code&gt;ok&lt;/code&gt;누르고 다시 우측 하단 &lt;code&gt;ok&lt;/code&gt;를 눌러서 무사히&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;474&quot; data-origin-height=&quot;231&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c9oyA5/btsHmRO9sto/wejbeqanPZMSK4hj8yUmx0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c9oyA5/btsHmRO9sto/wejbeqanPZMSK4hj8yUmx0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c9oyA5/btsHmRO9sto/wejbeqanPZMSK4hj8yUmx0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc9oyA5%2FbtsHmRO9sto%2FwejbeqanPZMSK4hj8yUmx0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;474&quot; height=&quot;231&quot; data-origin-width=&quot;474&quot; data-origin-height=&quot;231&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 데이터베이스가 생겼는지 확인해보자! 더블클릭 할 경우 우리가 잘 만든 &lt;code&gt;mysql_test&lt;/code&gt; 서버에 들어왔고 &lt;code&gt;test&lt;/code&gt; 데이터베이스가 &lt;code&gt;default&lt;/code&gt;로 되어 있는 걸 확인 할 수 있다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1933&quot; data-origin-height=&quot;1271&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0PRSi/btsHnWV8FQw/RPRtJlmRtMlcJkM5cZCKRk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0PRSi/btsHnWV8FQw/RPRtJlmRtMlcJkM5cZCKRk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0PRSi/btsHnWV8FQw/RPRtJlmRtMlcJkM5cZCKRk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0PRSi%2FbtsHnWV8FQw%2FRPRtJlmRtMlcJkM5cZCKRk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1933&quot; height=&quot;1271&quot; data-origin-width=&quot;1933&quot; data-origin-height=&quot;1271&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 모두 성공했다면 무사히 docker를 통해 mysql서버를 컴퓨터로 띄울 수 있었고 데이터베이스도 사용할 수 있게 된 것이다.&lt;br /&gt;그러면 다음 글에서 &lt;code&gt;next.js&lt;/code&gt; 와 &lt;code&gt;prisma&lt;/code&gt; 를 통해 데이터 베이스에 접근하고 사용할 수 있는지 테스트 해보자!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Reference: &lt;a href=&quot;https://mungto.tistory.com/328&quot;&gt;https://mungto.tistory.com/328&lt;/a&gt;&lt;/p&gt;</description>
      <category>업무</category>
      <category>docker</category>
      <category>MySQL</category>
      <category>Next.js</category>
      <category>Prisma</category>
      <author>모_아이</author>
      <guid isPermaLink="true">https://mo-i-programmers.tistory.com/85</guid>
      <comments>https://mo-i-programmers.tistory.com/85#entry85comment</comments>
      <pubDate>Mon, 13 May 2024 15:06:37 +0900</pubDate>
    </item>
    <item>
      <title>Ember -Tutorial (Service &amp;amp; Form input)</title>
      <link>https://mo-i-programmers.tistory.com/84</link>
      <description>&lt;h1&gt;Service&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엠버의 서비스는 애플리케이션이 동작하는 동안 유지되는 숫자 개체이며 애플리케이션의 다른부분에서 사용할 수 있다.&lt;br /&gt;번역 본이라 조금 이상한데 여러개의 컴포넌트에서 &lt;code&gt;service1&lt;/code&gt;을 사용할 수 있다는 뜻.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시로 로그인이라는 서비스를 통해 로그인 페이지에서 로그인 서비스로 로그인을 보낸뒤 사용자 페이지에서 받을 수 있도록 할 수 있다.&lt;br /&gt;개념적으로만 보면 전역 상태가 생각되는데 한번 사용 예시를 봐봐야 겠다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 service를 만드려면 동일하게 emberCLI로 만드는데&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;
ember g service shopping-cart&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러면 &lt;code&gt;app/services/shopping-cart.js&lt;/code&gt; 파일이 하나 만들어 진다&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;
// app/services/shopping-cart.js

import Service from '@ember/service'

export default class ShoppingCartService extends Service {
  itemList = [
  {name: '1'},
  {name: '2'}
 ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 &lt;code&gt;shopping-cart&lt;/code&gt;라는 &lt;code&gt;service&lt;/code&gt;에 &lt;code&gt;itemList&lt;/code&gt;라는 더미 데이터를 만들어 놓는다.&lt;br /&gt;이후 &lt;code&gt;controllers/cart.js&lt;/code&gt;로 가서 서비스를 주입한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 보고 있는 ember tutorial은 2022 영상이지만 현재 2024 docs에선 변경점이 있다&lt;br /&gt;&lt;code&gt;service&lt;/code&gt;를 &lt;code&gt;import&lt;/code&gt;할 때 2022 영상에선 &lt;code&gt;import {inject as service} from '@ember/service'&lt;/code&gt;처럼 사용하는 걸 볼 수 있는데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;inject&lt;/code&gt;를 &lt;code&gt;as&lt;/code&gt;를 통해 변경해서 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 최근 docs를 보면 업데이트 되었는지 &lt;code&gt;component&lt;/code&gt;에서 사용할 때 &lt;code&gt;import {service} from '@ember/service'&lt;/code&gt;로 사용되고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지 &lt;code&gt;import&lt;/code&gt;에 대한 얘기이고 사용에 관해서는 데커레이터를 사용하면 된다&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;// controllers/cart.js

export default class CartController extends Controller {
  @service shoppingCart;

  //...somecode
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &lt;code&gt;shopping-cart&lt;/code&gt;에서 선언한 더미데이터를 쓰기 위해 &lt;code&gt;template&lt;/code&gt;으로 가서&lt;/p&gt;
&lt;pre class=&quot;htmlbars&quot;&gt;&lt;code&gt;//...some code

{{#each this.shoppingCart.itemList as |item|}}
  //...some code
{{/each}}
///...some code&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 사용하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;만약 &lt;code&gt;service&lt;/code&gt;를 사용할 때 파일 이름이 아닌 현재 컨트롤러 안에서 내가 이름을 재정의 하고 싶다면 &lt;code&gt;service&lt;/code&gt; 데커레이터 사용하는 부분을 &lt;code&gt;@service shoppingCart&lt;/code&gt; 에서 &lt;code&gt;@service('shopping-cart') cart;&lt;/code&gt; 로 변경 해주면 된다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 함수 부분도 &lt;code&gt;service&lt;/code&gt; class 내에서 선언한뒤 사용부분에서 데커레이터를 통해 가져오고 똑같이 사용하면 된다.&lt;/p&gt;
&lt;h1&gt;Form Input&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 하나의 &lt;code&gt;input&lt;/code&gt;창을 가지고 있고 이걸 변경하고 싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시로 장바구니에 상품이 있고 상품 개수를 수동으로 조절할 수 있는 input이 있는데 작성해보자&lt;/p&gt;
&lt;pre class=&quot;htmlbars&quot;&gt;&lt;code&gt;&amp;lt;!-- cart.hbs --&amp;gt;
{{#each this.shoppingCart.itemList as |item|}}
  &amp;lt;input type=&quot;number&quot; value={{item.count}} {{on &quot;input&quot; (fn this.updateItemCount item)}}/&amp;gt;
{{/each}}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 &lt;code&gt;controllers/cart&lt;/code&gt;로 이동해서 &lt;code&gt;@action&lt;/code&gt;하나를 추가한다.&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt;//controllers/cart.js

//...some code
@action
updateItemCount(item, event) {
  const count = event.target.value;
  item.count = count;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 전달되는 &lt;code&gt;item&lt;/code&gt;은 &lt;code&gt;input&lt;/code&gt;에서 보내는 &lt;code&gt;item&lt;/code&gt;이며 2번째 매개변수인 &lt;code&gt;event&lt;/code&gt;는 말그대로 이벤트이다.&lt;br /&gt;따라서 js 기본적으로 실제 값 얻는 방식을 그대로 사용하면편한데 &lt;code&gt;event.target.value&lt;/code&gt; 사용하면 된다.&lt;br /&gt;이후 전달받은 &lt;code&gt;item&lt;/code&gt;의 &lt;code&gt;count&lt;/code&gt;를 현재입력된&lt;code&gt;count&lt;/code&gt;로 변경하면 된다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 주의할 점으론 &lt;code&gt;tracked&lt;/code&gt;되고 있는 놈은 &lt;code&gt;itemList&lt;/code&gt;로 ember는 &lt;code&gt;itmeList&lt;/code&gt; 내부의 &lt;code&gt;count&lt;/code&gt;가 변경 되었는지 확인할 수 없다.&lt;br /&gt;따라서 &lt;code&gt;itemList&lt;/code&gt;에 들어갈 &lt;code&gt;item&lt;/code&gt;을 하나의 &lt;code&gt;class&lt;/code&gt;로 만들고 거기서 &lt;code&gt;count&lt;/code&gt;를 &lt;code&gt;@tracked&lt;/code&gt;를 사용해 트랙킹 하고 이를 &lt;code&gt;itemList&lt;/code&gt;에 들어갈 &lt;code&gt;item&lt;/code&gt;들을 모두 &lt;code&gt;new Item&lt;/code&gt;을 통해 각각의 &lt;code&gt;item&lt;/code&gt;의 &lt;code&gt;count&lt;/code&gt;가 추적되고 있다면 값의 변함을 트랙킹하고 이를 실제로 웹에 반영한다.&lt;br /&gt;이를 통해 총 합을 구하는 부분에서도 연관된 부분이 &lt;code&gt;@tracked&lt;/code&gt;되어 같이 업데이트 되기 때문에 잘 고려해서 만들어보자&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 주의할 점은 &lt;code&gt;updateItemCount&lt;/code&gt;를 사용하는 부분에서 우리는 인자로 &lt;code&gt;item&lt;/code&gt; 하나만 넘겼는데 &lt;code&gt;class&lt;/code&gt;에서는 &lt;code&gt;event&lt;/code&gt; 매개변수가 추가로 있다.&lt;br /&gt;이를 통해 &lt;code&gt;on&lt;/code&gt;도우미를 사용하고 이벤트를 호출하는 경우 기본인수로 &lt;code&gt;event&lt;/code&gt;가 전달된다.&lt;/p&gt;</description>
      <category>Ember</category>
      <author>모_아이</author>
      <guid isPermaLink="true">https://mo-i-programmers.tistory.com/84</guid>
      <comments>https://mo-i-programmers.tistory.com/84#entry84comment</comments>
      <pubDate>Mon, 29 Apr 2024 15:48:03 +0900</pubDate>
    </item>
    <item>
      <title>Ember -Tutorial (Helper &amp;amp; custom Helper)</title>
      <link>https://mo-i-programmers.tistory.com/83</link>
      <description>&lt;h1&gt;Helper&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ember에는 helpler라는 개념이 있는데 이는 이전 글에서 &lt;code&gt;action&lt;/code&gt;을 사용할 때 이용했던 &lt;code&gt;on&lt;/code&gt;,&lt;code&gt;fn&lt;/code&gt; 등이 이런 helper에 해당된다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 한가지 다른게 보이는데 &lt;code&gt;on&lt;/code&gt;을 사용할 때는&lt;code&gt;{{}}&lt;/code&gt;안에서 사용했고 &lt;code&gt;fn&lt;/code&gt;은 &lt;code&gt;()&lt;/code&gt;안에서 사용됐다&lt;br /&gt;이는 최상위 수준에서의 도우미가 &lt;code&gt;{{}}&lt;/code&gt;로 시작하기 때문이다.&lt;br /&gt;&lt;code&gt;{{helper1, (helper2 ... (helper3 ....))}}&lt;/code&gt; 가장 최상위는 &lt;code&gt;{{}}&lt;/code&gt;로 시작하고 이후 중첩되는 도우미들은 &lt;code&gt;()&lt;/code&gt;로 둘러싼다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Built-in Helper&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ember에 내장된 도우미들 몇개만 살펴보자면&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;{{on&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{{concat&lt;/code&gt; :여러 문자열을연결하는데 도움이 된다. `class={{concat &quot;active-&quot; @color}}&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{{get&lt;/code&gt; : 가져오는 것으로 &lt;code&gt;{{get this.product &quot;name&quot;}}&lt;/code&gt;. 첫번째 인수는 해당 제품의 객체이고 2번째 인수는 그 객체의 키값이다. 객체의 키값으로 찾는 &lt;code&gt;product.name&lt;/code&gt;과 동일하고 뒤에 오는 &lt;code&gt;'name'&lt;/code&gt;은 동적으로도 가능하다&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{{hash&lt;/code&gt; : &lt;code&gt;&amp;lt;Child @user ={{hash firstName=&quot;Lee&quot; lastName=&quot;Moi&quot;}}&lt;/code&gt;처럼 주어진 키와 값 쌍으로 해시를 생성하는 도우미이다. 이는 내부적으로 &lt;code&gt;user = {firstName:'Lee', lastName:'Moi'}&lt;/code&gt;와 동일하다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{{let&lt;/code&gt; : &lt;code&gt;{{let (concat this.firstName this.lastName) as |fullName|}}&lt;/code&gt; 처럼 사용하면 템플릿 내부에 임시 변수로서 사용할 수 있게 만드는 도우미로 &lt;code&gt;concat&lt;/code&gt;도우미를 활용해 이름을 연결하여 &lt;code&gt;LeeMoi&lt;/code&gt;라는 문자열을 &lt;code&gt;fullName&lt;/code&gt;이라는 이름으로 사용할숭 있게 만드는 것이다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{{if&lt;/code&gt; : 프로그래밍에서 자주 보는 것으로 똑같다 &lt;code&gt;if&lt;/code&gt; 조건절로 &lt;code&gt;class={{if this.isRed 'red' 'black'}}&lt;/code&gt;이렇게 사용될 수 있다. 이는 &lt;code&gt;this.isRed&lt;/code&gt;가 조건이며 &lt;code&gt;red&lt;/code&gt;가 &lt;code&gt;true&lt;/code&gt;일 때, &lt;code&gt;black&lt;/code&gt;이 &lt;code&gt;false&lt;/code&gt;일 때 출력되는 값이다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{{unless&lt;/code&gt; : &lt;code&gt;class={{unless this.isRed 'black' 'red}}&lt;/code&gt; &lt;code&gt;if&lt;/code&gt;와 반대로 조건이 거짓인 경우 첫번째를 반환하고 참일경우 2번째를 반환한다. 우리가 자주 사용한 &lt;code&gt;!&lt;/code&gt;를 붙여 &lt;code&gt;false&lt;/code&gt;가 &lt;code&gt;true&lt;/code&gt;일때를 쓰는 방식. &lt;code&gt;unless&lt;/code&gt;도 똑같이 &lt;code&gt;else&lt;/code&gt;를사용할 수 있다(열린태그에서)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;{{each&lt;/code&gt; : React에서 가장 많이 사용했던 &lt;code&gt;map&lt;/code&gt;이라고 생각하자 &lt;code&gt;each&lt;/code&gt;를 통해 반복을 돌리고 하나씩 꺼내서 렌더링 하는데 도움을 준다&lt;br /&gt;아래 개방태그에서 마지막 예시를 확인해보자&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등이 있고 개방태그에서 사용할 수 있는 예시를 하나씩 보면 이해하기 편하다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의할 점으로 요소의 attribute 영역에서 사용하는 것을 제외하고 열려있는 구역에서 사용할 시 &lt;code&gt;#&lt;/code&gt;과 &lt;code&gt;/&lt;/code&gt;을 통해 꼭 표시를 해줘야한다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre class=&quot;htmlbars&quot;&gt;&lt;code&gt;{{#let (concat this.firstName this.lastName) as |fullName|}}
  &amp;lt;h1&amp;gt;{{fullName}}&amp;lt;/h1&amp;gt;
{{/let}}

{{#if this.isRed}}
  &amp;lt;h1&amp;gt;Red&amp;lt;/h1&amp;gt;
{{/if}}

{#if this.isRed}}
  &amp;lt;h1&amp;gt;Red&amp;lt;/h1&amp;gt;
{{else}}
  &amp;lt;h1&amp;gt;Black&amp;lt;/h1&amp;gt;
{{/if}}

{#unless this.isRed}}
  &amp;lt;h1&amp;gt;Black&amp;lt;/h1&amp;gt;
{{else}}
  &amp;lt;h1&amp;gt;Red&amp;lt;/h1&amp;gt;
{{/unless}}

{{#each list as |item|
  &amp;lt;h1&amp;gt;{{item.name}}&amp;lt;/h1&amp;gt;
  &amp;lt;h2&amp;gt;{{item.description}}&amp;lt;/h2&amp;gt;
{{/each}}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;each&lt;/code&gt;에 대한 설명을 조금만 더 붙이자면 &lt;code&gt;each&lt;/code&gt;를 &lt;code&gt;for&lt;/code&gt;와 동일하게 보며 &lt;code&gt;list&lt;/code&gt;는 반복을 돌릴 객체이며 &lt;code&gt;as |item|&lt;/code&gt;부분은 &lt;code&gt;let&lt;/code&gt;을 사용한 부분처럼 반복을 돌면서 나올 하나하나의 &lt;code&gt;item&lt;/code&gt;을 &lt;code&gt;item&lt;/code&gt;이라는 변수명으로 사용하겠다는 얘기이다.&lt;br /&gt;그리고 꼭 끝났다는 {{/each}}를 집어넣어 마무리 하자&lt;/p&gt;
&lt;h1&gt;custom helper&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 지정 도우미를 만들 수 있는데 이도 똑같이 emberCLI를 통해 만들 수 있다.&lt;br /&gt;또 나오긴 하지만 React에서 custom hook과 같은 느낌이 처음에 강하게 든 개념이다&lt;/p&gt;
&lt;pre class=&quot;delphi&quot;&gt;&lt;code&gt;ember g helper currency&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라고 저번에 만들었던 샵과 장바구니에서 가격을 나타내는 데 $가격으로 나타내고 싶은 것을 도우미로 만들어보려한다.&lt;br /&gt;처음 CLI를 입력하면 &lt;code&gt;currency.js&lt;/code&gt;라는 파일이 생성되는데 첫 내부를 살펴보면&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;import {helper} from '@ember/component/helper'

export default helper(function currency(params/*, hash*/) {
  return parmas
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 생겼다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 helper라는 한수에 인자로 우리가 CLI로 만들때 만든 이름인 &lt;code&gt;currency&lt;/code&gt;라는 함수를 만들어 넘긴다.&lt;br /&gt;이때 첫번째 인수인 &lt;code&gt;params&lt;/code&gt;는 도우미 블록에서 전달되는 매개변수 목록이다.&lt;br /&gt;즉, &lt;code&gt;{{currency 25}}&lt;/code&gt;를 작성할경우 &lt;code&gt;params&lt;/code&gt; 배열 내부의 첫번째 매개변수로 들어오게 된다.&lt;br /&gt;&lt;code&gt;const [number] = params;&lt;/code&gt; 처럼 구조파괴 하여 사용가능하고 이를 통해 &lt;code&gt;number&lt;/code&gt;에는 &lt;code&gt;25&lt;/code&gt;라는 값이 담기게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 주석 처리 되어 있는 &lt;code&gt;hash&lt;/code&gt;부분은 2번째 매개변수로 무엇일까??&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키가 정의되어 helper에게 값을 전달 할 수 있는 해시이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이또한 어떻게 사용하는지 바로 보자면 &lt;code&gt;{{currency 25 sign=&quot;$&quot;}}&lt;/code&gt; 처럼 사용할 수 있는데 이 또한 &lt;code&gt;hash&lt;/code&gt;를 구조분해 할당하여&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;const {sign} = hash&lt;/code&gt;를 통해 안에 &lt;code&gt;sign&lt;/code&gt;이라는 키값으로 보내진 값 &lt;code&gt;$&lt;/code&gt;를 사용할 수 있게 된다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뭐 기본값을 설정하거나 하는 등의 테크닉은 Javascript를 안다면 충분히 해볼 수 있다.&lt;br /&gt;매개변수를 주지 않고 &lt;code&gt;currency&lt;/code&gt;의 함수에서 &lt;code&gt;=&lt;/code&gt;등을 사용해 기본값을 설정해서 사용한다면 고정적인 값들을 사용할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;근데 꼭 함수 기반으로 해야할까?&lt;/b&gt; 이는 함수형 프로그래밍인 Javascript이긴 하지만 ember는 class가 대부분이다&lt;br /&gt;이&lt;code&gt;curreny&lt;/code&gt;도 비슷하게 갈 수 있지 않을까??&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;class helper로 변경&lt;/h2&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;import Helper from '@ember/component/helper'

export default class currency extends Helper {
  compute(params,hash) {
    //...some code
    return /*...some code*/
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내장되어 있는 &lt;code&gt;compute&lt;/code&gt; 메서드를 사용하여 활용할 수 있다.&lt;/p&gt;</description>
      <category>Ember</category>
      <author>모_아이</author>
      <guid isPermaLink="true">https://mo-i-programmers.tistory.com/83</guid>
      <comments>https://mo-i-programmers.tistory.com/83#entry83comment</comments>
      <pubDate>Mon, 29 Apr 2024 13:55:38 +0900</pubDate>
    </item>
    <item>
      <title>Ember - Tutorial(Component#2-Tracked,Getter,Actions)</title>
      <link>https://mo-i-programmers.tistory.com/82</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;제목에 적어놓은 Tracked, Getter, Actions는 컴포넌트에만 존재하는 것이 아니라 Controller에서 사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뿐만아니라 관련해서는 현재 튜토리얼 페이지에서 따라해보면 Route에서도 사용되는 것 같다.&lt;/p&gt;
&lt;h1&gt;Tracked properties &amp;amp; Getter&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 컴포넌트의 구성부터 살펴보자면&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;import Component from '@glimmer/component'
import {tracked} from '@glimmer/trancking'

export default class MyComponent extends Component {
  @tracked firstName = 'Shawn';

  @tracked lastName = 'Chen'

  get fullName() {
    return this.firstName + this.lastName;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;glimmer&lt;/code&gt;에서 &lt;code&gt;import&lt;/code&gt;시킨 &lt;code&gt;tracked&lt;/code&gt;는 데코레이터로 속성 앞에 작성할 경우 동작한다.&lt;br /&gt;어떤 것에 대한 동작이냐?&lt;br /&gt;간단히 설명하면 React에서 사용했던 useState같은 느낌과 비슷하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 React를 사용할 때 상태가 변경됨을 체크하려면 useState를 썼던 것처럼 속성이 변경될 때 알림을 받으려면 &lt;code&gt;@tracked&lt;/code&gt; 즉, &lt;code&gt;GIT&lt;/code&gt;을 할 때 한번쯤 들었던 것처럼 트랙킹,추적을 해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이경우 &lt;code&gt;hbs&lt;/code&gt; 파일에서&lt;/p&gt;
&lt;pre class=&quot;django&quot;&gt;&lt;code&gt;&amp;lt;h1&amp;gt;{{this.fullName}}&amp;lt;/h1&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용했을 때 fullName을 렌더링 해야한다면 일단 처음 만들어질 때 초기화 된 &lt;code&gt;Shawn Chen&lt;/code&gt;을 출력할 것이다.&lt;br /&gt;&lt;code&gt;tracked&lt;/code&gt;되어 있기 때문에 아마 변경이 된다면 최신 정보를 출력할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 최신으로 바꾸기 위해 필요한 &lt;code&gt;Action&lt;/code&gt;부분을 살펴보자&lt;/p&gt;
&lt;h1&gt;Action&lt;/h1&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;import Component from '@glimmer/component'
import {tracked} from '@glimmer/trancking'
import {action} from '@ember/object'

export default class MyComponent extends Component {
  @tracked number = 0;

  @action
  addNumber() {
    this.number = this.number + 1;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;action&lt;/code&gt;데커레이터를 통해서 숫자를 증가시키는 간단한 액션을 추가한다. 이후 &lt;code&gt;template&lt;/code&gt;부분에서 보자면&lt;/p&gt;
&lt;pre class=&quot;htmlbars&quot;&gt;&lt;code&gt;&amp;lt;h1&amp;gt;Number : {{this.number}}&amp;lt;/h1&amp;gt;
&amp;lt;button {{on &quot;click&quot; this.addNumber}}&amp;gt;Add&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 React 등을 보다 보면 되게 생소할 수 잇다. 저게 함수인가?? 뭐지? 하는 느낌이 나도 처음 들었는데 일단 하나씩 살펴보자면..&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;on&lt;/code&gt;부분은 이벤트를 트리거 할 수 있도록 도와주는 ember helper인데 이 부분인데 정확한 설명은 없고 일단 버튼에 이벤트를 트리거 하기 위해선 첫번째로 on이 들어가는 구나를 일단 인식해보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2번째 인수로 들어간 &lt;code&gt;&quot;click&quot;&lt;/code&gt;은 이벤트의 이름을 알려주고 클릭임으로 클릭이벤트를 트리거 한다라고 알려주는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;그리고 마지막 이벤트는 우리가 정의한 액션을 집어 넣는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;on&lt;/code&gt;을 통해 ember helper를 불러서 ember가 이벤트를 트리거 할 수 있도록 부르고 &lt;code&gt;&quot;click&quot;&lt;/code&gt;어떤 이벤트인지 알려주고, &lt;code&gt;this.addNumber&lt;/code&gt;실행할 이벤트를 적는 것이다.&lt;br /&gt;&lt;code&gt;onClick={clickHandler}&lt;/code&gt;를 &lt;code&gt;on/Click/={clickHandler}&lt;/code&gt;react에서 사용된 클릭 이벤트를 저렇게 나눠 쓴다고 생각하면 바로 이해가 온다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이걸 이전에 만든 컴포넌트들에 적용시키려면 어떻게 해야할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;똑같이 emberCLI를 이용한다면 만들기 쉽다&lt;/p&gt;
&lt;pre class=&quot;angelscript&quot;&gt;&lt;code&gt;ember g component-class product&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 입력할 경우 &lt;code&gt;product&lt;/code&gt;의 &lt;code&gt;class&lt;/code&gt;가 담긴 &lt;code&gt;app/components/product.js&lt;/code&gt;가 생성되게 된다.&lt;br /&gt;해당 파일에서 &lt;code&gt;tracked&lt;/code&gt;와 &lt;code&gt;action&lt;/code&gt;을 이용해 다양한 함수 처리를 해보고 익혀보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 속성과 인수부분은 어떻게 될까??&lt;/p&gt;
&lt;h1&gt;Properties &amp;amp; Arguments&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 속성은 현재 정의된 변수라 보면 편한데&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;export default class Child extends Component {
  propA = 1;
}

export default class Parent extends Component {
  propB = 2;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클래스와 인수는 상위 구성요소에서 전달된 것이므로 예시로 바로 확인해보자&lt;/p&gt;
&lt;pre class=&quot;django&quot;&gt;&lt;code&gt;&amp;lt;!-- parent.hbs --&amp;gt;
&amp;lt;Child @propB={{this.propB}} /&amp;gt;

&amp;lt;!-- child.hbs --&amp;gt;
&amp;lt;h1&amp;gt;Child : {{this.propA}}&amp;lt;/h1&amp;gt;

&amp;lt;h1&amp;gt;Parent : {{@propB}}&amp;lt;/h1&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇듯 상위에 있는 &lt;code&gt;parent.hbs&lt;/code&gt;에서 &lt;code&gt;Child&lt;/code&gt;컴포넌트에게 React처럼 &lt;code&gt;props&lt;/code&gt;를 보내고 싶을 때 &lt;code&gt;@&lt;/code&gt;를 사용해서 보내면 된다&lt;br /&gt;&lt;code&gt;@&lt;/code&gt;뒤에 보낼 prop에 이름을 명시하고 &lt;code&gt;={{this.propB}}&lt;/code&gt;으로 &lt;code&gt;Parent&lt;/code&gt;컴포넌트가 렌더링 될 당시 실행될 &lt;code&gt;class Parent&lt;/code&gt;에 있는 속성 &lt;code&gt;propB&lt;/code&gt;의 값 &lt;code&gt;2&lt;/code&gt;를 &lt;code&gt;Child&lt;/code&gt;에게 보낸다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러면 &lt;code&gt;Child.hbs&lt;/code&gt;에선 &lt;code&gt;propB&lt;/code&gt;라는 이름으로 &lt;code&gt;2&lt;/code&gt;라는 값을 받아 사용할 수 있게 되고 그사용되는 부분이 &lt;code&gt;{{@propB}}&lt;/code&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React에서 props로 어떠한 값을 보낼대 사용한 &lt;code&gt;&amp;lt;Child data={user} /&amp;gt;&lt;/code&gt;에서 &lt;code&gt;@&lt;/code&gt;와 &lt;code&gt;{}&lt;/code&gt;만 더 추가된 느낌이라 크게 어려울 것은 없다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;css에서도 똑같이 동적인클래스를 만들 수 있다&lt;br /&gt;&lt;code&gt;@tarcked&lt;/code&gt;된 속성을 변경됨에 따라 클래스를 변경할 수 있는데, 이는 &lt;code&gt;class&lt;/code&gt;에서도 똑같이 &lt;code&gt;{{}}&lt;/code&gt;를 사용해서 classname을 동적으로 줄 수 있다.&lt;br /&gt;이는 만약 텍스트 샊깔을 바꾼다 했을 때 기본 프로퍼티로는 &lt;code&gt;@tracked color = black;&lt;/code&gt;을 설정해놓은 상태다&lt;br /&gt;그리고 변경버튼을 누를 경우 &lt;code&gt;red&lt;/code&gt;색깔로 바뀐다고 가정했을 때 변경될 곳에&lt;br /&gt;&lt;code&gt;class=&quot;text-{{this.color}}&lt;/code&gt;식으로 넣을경우 동적으로 클래스 네임을 변경할 수 있게 된다.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 클릭을 눌렀을 때 어떠한 매개변수나 인자를 받고 싶다면 어떻게 하는게 좋을까?&lt;br /&gt;&lt;code&gt;{{on 'click' this.changeColor}}&lt;/code&gt; 이렇게 사용되는 걸 &lt;code&gt;{{on 'click' this.changeColor('red')}}&lt;/code&gt; 이렇게 바꾸면 될까?&lt;br /&gt;이때는 함수도우미를 사용하면 인자를 넘겨줄 수 있게 된다&lt;/p&gt;
&lt;pre class=&quot;hsp&quot;&gt;&lt;code&gt;&amp;lt;button {{on &quot;click&quot; (fn this.changeColor &quot;red&quot;)}}&amp;gt;ChangeRed&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 함수 도우미인 &lt;code&gt;fn&lt;/code&gt;을 사용하고 함수명 뒤에 적는 것이 인자로 넘어가게 된다.&lt;br /&gt;여러개라면 &lt;code&gt;{{on &quot;click&quot; (fn this.changeColor 인자1 인자2 인자3)}}&lt;/code&gt;으로 넘겨주면 동일하게 동작한다.&lt;br /&gt;&lt;b&gt;주의할 점으론 javascript 함수 법칙에 따라 인자와 매개변수의 위치에 대해 주의하자. 이는 typescript를 쓴다면 명확하게 코딩을 할 때 잡을 수 있는 부분이기도 하다&lt;/b&gt;&lt;/p&gt;</description>
      <category>Ember</category>
      <author>모_아이</author>
      <guid isPermaLink="true">https://mo-i-programmers.tistory.com/82</guid>
      <comments>https://mo-i-programmers.tistory.com/82#entry82comment</comments>
      <pubDate>Fri, 26 Apr 2024 15:15:58 +0900</pubDate>
    </item>
    <item>
      <title>Ember - Tutorial(Component#1)</title>
      <link>https://mo-i-programmers.tistory.com/81</link>
      <description>&lt;h1&gt;Component&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재사용 가능한 독립된 모듈로 주로 React를 한번쯤은 다뤄봤다면 아주 흔한 개념이다.&lt;br /&gt;이는 Ember에서도 동일하게 있는데 한번 알아보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터 내부나 다른 컴포넌트 또 그 컴포넌트 안에 컴포넌트 이렇게 React처럼 중첩이 가능하다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;예시&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 index route에 있다고 가정했을 때 제품목록 안에 2개의 제품이 있다&lt;br /&gt;그럼 각 제품 카드들의 프로덕트 모양은 동일할텐데 이미지 관련 로직이나 다른 구성들이 동일하게 동작한다면 이는 중복됨으로 하나의 컴포넌트로 처리하는 것이 맞다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접적인 전체 코드를 가져오진 않고 &lt;code&gt;component&lt;/code&gt;를 어떻게 만들고 어떻게 사용하는 지에대해서 간단하게 다루고 넘어가보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컴포넌트를 처음 만들때도 동일하게 emberCLI를 사용해 만들 예정이다&lt;/p&gt;
&lt;pre class=&quot;axapta&quot;&gt;&lt;code&gt;ember g component general-container&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러면 &lt;code&gt;app/components/general-container.hbs&lt;/code&gt;라는 파일이 생성되게 된다.&lt;br /&gt;이를 통해 &lt;code&gt;general-container.hbs&lt;/code&gt;라는 컴포넌트를 만들고 다른 곳에서도 동일하게 컴포넌트를 통해 동일한 로직과 기능을 재사용 할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 저번 글에서 만들었던 장바구니로 가는 버튼을 컴포넌트로 만든다면&lt;/p&gt;
&lt;pre class=&quot;handlebars&quot;&gt;&lt;code&gt;&amp;lt;!-- app/components/general-container.hbs--&amp;gt;

&amp;lt;LinkTo @route=&quot;cart&quot; class=&quot;cart_link&quot;&amp;gt;
  cart
&amp;lt;/LinkTo&amp;gt;

&amp;lt;main class=&quot;container mt-5&quot;&amp;gt;
  {{yield}}
&amp;lt;/main&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 추가 하게 됐는데 아까 &lt;code&gt;index&lt;/code&gt;에서 사용하던 페이지 모양하고 동일하며 코드 중간을 보면 &lt;code&gt;{{yield}}&lt;/code&gt;라는 특이한 게 있다.&lt;br /&gt;이는 쉽게 말하면 React에서 사용하던 &lt;code&gt;{children}&lt;/code&gt;과 같은 것으로 외부에서 해당 위치의 구성요소를 직접적으로 컨트롤 할 수 있게 되는 것이다.&lt;br /&gt;이를 통해 공통적인 부분은 두고 나머지 변할 수 있는 부분의 확장성을 고려한 컴포넌트를 작성할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이걸 어떻게 사용해야 하는가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 React를 사용할 때는 함수형 컴포넌트를 만들고 이를 &lt;code&gt;export&lt;/code&gt;시킨 후 &lt;code&gt;import&lt;/code&gt;하여 사용하였는데 ember는 따로 import하는 부분이 존재하진 않는다&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!-- app/templates/index.hbs --&amp;gt;

&amp;lt;GeneralContainer&amp;gt;
  &amp;lt;LinkTo @route=&quot;item&quot; @model=&quot;1&quot;&amp;gt;Product 1&amp;lt;/LinkTo&amp;gt;
  &amp;lt;LinkTo @route=&quot;item&quot; @model=&quot;2&quot;&amp;gt;Product 2&amp;lt;/LinkTo&amp;gt;
&amp;lt;/GeneralContainer&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;import&lt;/code&gt;도 없이 그냥 코드 자체를 이렇게 작성할 경우 이전에 작성한 코드랑 동일하게 동작하는 것을 볼 수 있다.&lt;br /&gt;작성된 코드를 보면 굉장히 특이한데 component의 파일 명은 &lt;code&gt;general-container&lt;/code&gt;인데 사용되는 부분은 &lt;code&gt;&amp;lt;GeneralContainer&amp;gt;&lt;/code&gt;로 사용되고 있다.&lt;br /&gt;이를 통해 코드 베이스 내부에 대시 케이스가 있더라도 엠버 구성요소 이름이 어떻게 되어 있는지 알 수 있다.(첫글자는 대글자여야 하고 카멜케이스로 작성해야한다)&lt;br /&gt;이를 통해 &lt;code&gt;item&lt;/code&gt; 페이지도 변경하자면&lt;/p&gt;
&lt;pre class=&quot;django&quot;&gt;&lt;code&gt;&amp;lt;!-- app/tmeplates/item.hbs --&amp;gt;
&amp;lt;GeneralContainer&amp;gt;
  ITEM :{{this.model}}
  product
&amp;lt;/GeneralContainer&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이도 &lt;code&gt;index&lt;/code&gt;처럼 동일하게 동작하는 걸 볼 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 &lt;code&gt;{{yeild}}&lt;/code&gt; 말고 React에서 자식 컴포넌트에게 필요한 데이터를 넘겨주던 &lt;code&gt;props&lt;/code&gt;방식은 어떻게 사용할까??&lt;br /&gt;이는 Product 상세페이지로 이동하는 링크 컴포넌트도 하나 만들어서 테스트해보자&lt;br /&gt;똑같이 emberCLI를 사용해 component를 하나 만든다&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;ember g component product&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!-- app/coomponents/product --&amp;gt;
&amp;lt;LinkTo @route=&quot;item&quot; @model=&quot;1&quot; class=&quot;product&quot;&amp;gt;
  &amp;lt;div class=&quot;product-image&quot;&amp;gt;
    &amp;lt;img src=&quot;/assets/images/headphone.png&quot; alt=&quot;&quot; /&amp;gt;
  &amp;lt;/div&amp;gt;
  &amp;lt;div class=&quot;product-details&quot;&amp;gt;
    &amp;lt;h3&amp;gt;title&amp;lt;/h3&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/LinkTo&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;product&lt;/code&gt;컴포넌트를 만들다 보니 &lt;code&gt;image&lt;/code&gt;가 들어가는 부분이 생기는데 이 부분도 컴포넌트를 하나 만들어보자. 이때의 컴포넌트는 &lt;code&gt;product&lt;/code&gt;의 컴포넌트로 사용하기 위해&lt;/p&gt;
&lt;pre class=&quot;maxima&quot;&gt;&lt;code&gt;ember g component product/image&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러면 파일 구조가 &lt;code&gt;components/product/image.hbs&lt;/code&gt;로 하나가 생기는데 이러면 똑같이 &lt;code&gt;&amp;lt;Image/&amp;gt;&lt;/code&gt; 이런식으로 사요하면 될까?&lt;br /&gt;조금 다르다&lt;br /&gt;일단 &lt;code&gt;product&lt;/code&gt;에서 작성한 &lt;code&gt;img&lt;/code&gt;태그 부분을 잘라내서 Image 컴포넌트에 옮긴 뒤 실제로 사용할 때는&lt;/p&gt;
&lt;pre class=&quot;xml&quot;&gt;&lt;code&gt;&amp;lt;!-- app/coomponents/product --&amp;gt;

&amp;lt;LinkTo @route=&quot;item&quot; @model=&quot;1&quot; class=&quot;product&quot;&amp;gt;
  &amp;lt;Product::Image /&amp;gt;
  &amp;lt;div class=&quot;product-details&quot;&amp;gt;
    &amp;lt;h3&amp;gt;title&amp;lt;/h3&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/LinkTo&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 식으로 &lt;code&gt;&amp;lt;Produt::Image/&amp;gt;&lt;/code&gt; &lt;code&gt;::&lt;/code&gt;를 사용해서 접근할 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 동일하게 detail 부분에도 사용한다면 emberCLI통해 component를 만들고 &lt;code&gt;Product::Detail&lt;/code&gt;을 통해 컴포넌트를 사용할 수 있다&lt;/p&gt;</description>
      <category>Ember</category>
      <author>모_아이</author>
      <guid isPermaLink="true">https://mo-i-programmers.tistory.com/81</guid>
      <comments>https://mo-i-programmers.tistory.com/81#entry81comment</comments>
      <pubDate>Thu, 25 Apr 2024 17:26:22 +0900</pubDate>
    </item>
    <item>
      <title>Ember - Tutorial(Router &amp;amp; controller)</title>
      <link>https://mo-i-programmers.tistory.com/80</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;라우팅 시스템과 Router와 controller는 밀접한 연관이 있으니 바로 다뤄보는 게 좋을 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유튜브 ember tutorial에서 만드는 페이지로 연습을 같이 해보려고 하는데 일단 이전에 만든 필요없는 경로를 삭제해보자&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;router 삭제&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;당연하게도 이를 도와주는 emberCLI가 존재하는데&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;
ember destroy route clothes/index &amp;amp;&amp;amp; ember destroy route clothes/t-shirt &amp;amp;&amp;amp; ember destroy route clothes&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;clothes&lt;/code&gt;경로에는 중첩된 부분이 많기 때문에 중첩된걸하나하나 지워가며 마지막에 가장 큰 경로를 삭제하는 게 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 큰 경로를 삭제해봐야 폴더 내부는 안지워지고 `clothes.hbs`와 테스트 등 `clothes` 경로에 관한것만 지워지지 `clothes`폴더에 있는 중첩된 경로들까지 삭제해주진 않는다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;새 router 추가&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 쓸모없는 경로들을 삭제했으니 이제 웹 페이지에 들어갈만한 경로들을 만들자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 홈페이지인 애플리케이션 인덱스 페이지 경로인 인덱스를 생성하고 장바구니로 쓸 cart부분을 만들자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;물론 emberCLI로 만든다&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ember g route index &amp;amp;&amp;amp; ember g route cart&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &lt;code&gt;cart&lt;/code&gt;경로의 경우 실제 경로 이름에선 좀 더 명확하게 해주기 위해서 다룰 파일의 이름은 쉽고 경로 이름은 명확하게 서로 다른 옵션을 사용하기 위해 &lt;code&gt;router.js&lt;/code&gt;에서 변경해준다&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;Router.map(function () {
  this.route('item', { path: 'item/:item_id' });
  this.route('not-found', { path: '/*path' });
  this.route('cart', { path: 'shopping-cart' });
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 입력할 경우 경로는 &lt;code&gt;/shopping-cart&lt;/code&gt;가 되지만 실제 동작은 &lt;code&gt;cart&lt;/code&gt;가 받아서 하게된다&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이때 index route는 왜 없나요?? 할 수 있다&lt;/b&gt;&lt;br /&gt;이는 index일 경우 default 페이지 역할을 하기 때문에 최상단 &lt;code&gt;application.hbs&lt;/code&gt; 와 동일한 뎁스에 &lt;code&gt;index.hbs&lt;/code&gt;를 가졌음으로 맨 처음 뒤에 어떠한 추가 경로도 적지 않고 말 그대로 홈페이지에 접속할 경우 이 해당 &lt;code&gt;index.hbs&lt;/code&gt;로 접속할 수 있다.&lt;br /&gt;현재 로컬에선 &lt;code&gt;http://localhost:4200&lt;/code&gt;으로 접속시 &lt;code&gt;application.hbs&lt;/code&gt;의 &lt;code&gt;{{outlet}}&lt;/code&gt;부분에 &lt;code&gt;index.hbs&lt;/code&gt;가 뜨게 되는 것이다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Navigate pages&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 홈페이지로 표현될 index.js에 상품 목록들을 보이게 하고 상품 목록들을 클릭하면 상품 상세보기로 점프 할 수 있게 하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 application.hbs에 있는 코드들을 {{outlet}}을 제외하고 모두 지운 뒤 index.js에서 원하는 목록모양으로 HTML작성한다&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;&amp;lt;main class=&quot;container mt-5&quot;&amp;gt;
  &amp;lt;div&amp;gt;Product 1&amp;lt;/div&amp;gt;
  &amp;lt;div&amp;gt;Product 2&amp;lt;/div&amp;gt;
&amp;lt;/main&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단은 이렇게 작성하였는데 우리가 원하는 건 저 Product 1부분을 클릭했을 때 해당 제품의 상세페이지로 점프하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 어떻게 만들 수 있을까?&lt;br /&gt;Next.js로 할땐 Link라는 녀석이 있었는데 ember도 비슷한 놈이 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;바로 LinkTo이다&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 Next의 Link 처럼 href로 경로를 나타내냐? 이건 또 아니고 @route라는 걸 사용한다&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;&amp;lt;main class=&quot;container mt-5&quot;&amp;gt;
  &amp;lt;LinkTo @route=&quot;item&quot; @model=&quot;1&quot;&amp;gt;Product 1&amp;lt;/LinkTo&amp;gt;
  &amp;lt;LinkTo @route=&quot;item&quot; @model=&quot;2&quot;&amp;gt;Product 2&amp;lt;/LinkTo&amp;gt;
&amp;lt;/main&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 제품을 클릭할 때 이동되는 경로는 item이 되고 이동할때 @model=&quot;1&quot;을 통해 id값을 넘겨주게 된다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 LinkTo로 바꾼 페이지 모양은 html변환시 a태그와 동일하다&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1549&quot; data-origin-height=&quot;426&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/3l5KM/btsGVsgAfo7/7JaKd3xjEALKe2apiwzfc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/3l5KM/btsGVsgAfo7/7JaKd3xjEALKe2apiwzfc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/3l5KM/btsGVsgAfo7/7JaKd3xjEALKe2apiwzfc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F3l5KM%2FbtsGVsgAfo7%2F7JaKd3xjEALKe2apiwzfc1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1549&quot; height=&quot;426&quot; data-origin-width=&quot;1549&quot; data-origin-height=&quot;426&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;982&quot; data-origin-height=&quot;171&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/w16gV/btsGTfbInuF/kfdIpNkoyUaNTaQxWq6a6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/w16gV/btsGTfbInuF/kfdIpNkoyUaNTaQxWq6a6k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/w16gV/btsGTfbInuF/kfdIpNkoyUaNTaQxWq6a6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fw16gV%2FbtsGTfbInuF%2FkfdIpNkoyUaNTaQxWq6a6k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;982&quot; height=&quot;171&quot; data-origin-width=&quot;982&quot; data-origin-height=&quot;171&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 &lt;code&gt;LinkTo&lt;/code&gt;를 통해 각 경로를 이동할 수 있고 &lt;code&gt;.hbs&lt;/code&gt;에 원하는&lt;code&gt;hmtl&lt;/code&gt;을 작성하여 템플릿을 렌더링 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 통해 각 cart와 product부분을 만든 후 &lt;code&gt;cart.hbs&lt;/code&gt;에서 결제를 시킬 &lt;code&gt;check out&lt;/code&gt;이라는 버튼을 한번 만들어보자.&lt;/p&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;  &amp;lt;button type=&quot;button&quot; class=&quot;btn btn-success float-right&quot;&amp;gt;Check out&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 일단 button 은 만들어 놓고 버튼이 클릭 되었을 때 동작해야할 기능들은 어디에 적어야 할까?&lt;br /&gt;기존 react나 next같은 경우 현재 함수형 컴포넌트들을 통해 같은 함수 스코프 안에 return 상단에 여러 기능들을 하는 함수들을 적어서 &lt;code&gt;onClick&lt;/code&gt;에 넣는 식으로 사용했었는데 ember는 좀 모양새도 많이 달랐다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 먼저 Route와 Controller를 알아보고 추후에 추가해보자&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;Router와 Controller&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우터와 컨트롤러의 차이점은 동일한 URL에 대해 작동하고 이름도 같지만 다른 폴더에 존재한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Route
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;route/cart.js&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Controller
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;controller/cart.js&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 서로 &lt;code&gt;cart.js&lt;/code&gt;라는 같은 이름을 가지지만 각각 &lt;code&gt;Route&lt;/code&gt;와 &lt;code&gt;Controller&lt;/code&gt;라는 다른 폴더에 존재한다.&lt;br /&gt;둘 다 app 경로 아래에 존재하며 &lt;code&gt;template/cart.hbs&lt;/code&gt;를 렌더링한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Route에선 controller에 모델을 관여할 수 있으며 다양한 Methods들이 존재하는데 이들을 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한번 다이나믹 라우팅을 사용하는 item은 product &lt;code&gt;LinkTo&lt;/code&gt;로 이동할 때 &lt;code&gt;id&lt;/code&gt;도 보내는데 이를 어떻게 받을까?&lt;br /&gt;지난 글에서 썼었던? &lt;code&gt;route/item.js&lt;/code&gt;를 활용해보자&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;// app/route/item.js
import Route from '@ember/routing/route';

export default class ItemRoute extends Route {
  model(params) {
    const { item_id } = params;

    return item_id;
  }
}

// app/templates/item.hbs
&amp;lt;h2&amp;gt;{{this.model}} product&amp;lt;/h2&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;this.model&lt;/code&gt;처럼 사용할 수 있는 건데 이걸 통해 &lt;code&gt;routes/cart.js&lt;/code&gt;도 변경해보자&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import Route from '@ember/routing/route';

export default class CartRoute extends Route {
  model() {
    const items = [{ price: 10 }, { price: 15 }];
    return items;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 이런식으로 여러 가격을 가지고 있는 상품들이 있다고 생각하고 작성해보았다&lt;br /&gt;그리고 controller를 사용해보려고 하는데 우리는 아직 만들지 않았으니 ember CLI로 만들어보자.&lt;/p&gt;
&lt;pre class=&quot;ebnf&quot;&gt;&lt;code&gt;ember g controller cart&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 &lt;code&gt;controller&lt;/code&gt;에 &lt;code&gt;cart&lt;/code&gt;를 만든후 템플릿에 전달할 몇가지 사용자 정의 속성을 만들어보자&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;// app/contorollers/cart.js
import Controller from '@ember/controller';

export default class CartController extends Controller {
  subtotla = 0;
  tax = 0;
  total = 0;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;cart&lt;/code&gt;라는 장바구니 페이지에서 사용될 상품가격, 세금, 총 가격을 &lt;code&gt;CartController&lt;/code&gt;에 기본적으로 정의하고 &lt;code&gt;templates&lt;/code&gt;에 있는 &lt;code&gt;cart.hbs&lt;/code&gt;에 가서 정의된 속성들을 사용해보자&lt;/p&gt;
&lt;pre class=&quot;cs&quot;&gt;&lt;code&gt; &amp;lt;section class=&quot;w-50 ml-auto text-right mb-5&quot;&amp;gt;
    &amp;lt;div class=&quot;row&quot;&amp;gt;
      &amp;lt;span class=&quot;col&quot;&amp;gt;Subtotal&amp;lt;/span&amp;gt;
      &amp;lt;span class=&quot;col&quot;&amp;gt;{{this.subtotal}}&amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div class=&quot;row&quot;&amp;gt;
      &amp;lt;span class=&quot;col&quot;&amp;gt;Tax&amp;lt;/span&amp;gt;
      &amp;lt;span class=&quot;col&quot;&amp;gt;{{this.tax}}&amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;div class=&quot;row&quot;&amp;gt;
      &amp;lt;span class=&quot;col&quot;&amp;gt;Total&amp;lt;/span&amp;gt;
      &amp;lt;span class=&quot;col&quot;&amp;gt;{{this.total}}&amp;lt;/span&amp;gt;
    &amp;lt;/div&amp;gt;
  &amp;lt;/section&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;code&gt;{{this.subtotal}}&lt;/code&gt;사용 된 &lt;code&gt;this&lt;/code&gt;는 현재 컨트롤러 또는 현재 라우팅을 가리키고 이를 저장하고 앱을 실행하면 표시되는 페이지가&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;327&quot; data-origin-height=&quot;142&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bGpDRy/btsGWO4q9cO/kvPoJ5fxEDVnQJmvvLzUH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bGpDRy/btsGWO4q9cO/kvPoJ5fxEDVnQJmvvLzUH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bGpDRy/btsGWO4q9cO/kvPoJ5fxEDVnQJmvvLzUH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbGpDRy%2FbtsGWO4q9cO%2FkvPoJ5fxEDVnQJmvvLzUH1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;327&quot; height=&quot;142&quot; data-origin-width=&quot;327&quot; data-origin-height=&quot;142&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 표시되는 것을 볼 수 있다.&lt;br /&gt;이 숫자 0들은 우리가 &lt;code&gt;Contoroller&lt;/code&gt;에서 정의한 값들이다. 하지만 아직 정적인 값들이니 이 값들을 실제로 관리해보기 위해 &lt;code&gt;route/cart.js&lt;/code&gt;로 이동해서 코드를 추가해보자&lt;/p&gt;
&lt;pre class=&quot;scala&quot;&gt;&lt;code&gt;// routes/cart.js

import Route from '@ember/routing/route';

export default class CartRoute extends Route {
  model() {
    const items = [{ price: 10 }, { price: 15 }];
    return items;
  }

  setupController(controller, model) {
    super.setupController(controller, model);
    const subtotal = model.reduce((acc, item) =&amp;gt; acc + item.price, 0);
    controller.set('subtotal', subtotal);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 먼저 &lt;code&gt;setupController&lt;/code&gt;를 통해 컨트롤러 기능을 재정의 하는 메서드를 사용한다.&lt;br /&gt;이때 &lt;code&gt;setupController&lt;/code&gt;는 이미 있는 걸로 재정의 할 때 매개변수는 &lt;code&gt;controller,model&lt;/code&gt; 을 사용할 것이다.&lt;br /&gt;그리고 &lt;code&gt;super&lt;/code&gt;를 사용해 &lt;code&gt;super.setupController()&lt;/code&gt;로 상속된 모든 항목이 호출됐는지 확인해본다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 &lt;code&gt;subtotal&lt;/code&gt;을 계산하기 위해 &lt;code&gt;model&lt;/code&gt;을 가져온다. 이때 모델은 위에서 return 된 items가 된다.&lt;br /&gt;이후 &lt;code&gt;controller.set&lt;/code&gt;을 통해 &lt;code&gt;controller&lt;/code&gt;에서 정의된 &lt;code&gt;subtotal&lt;/code&gt;의 값을 변경한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 컨트롤러가 모델에 직접적으로 엑세스 할 수 있는 걸 발견했으니 굳이 &lt;code&gt;route&lt;/code&gt;에서 작업하지 않고 바로 &lt;code&gt;controller&lt;/code&gt;에서 작업이 가능하단 걸 깨달을 수 있다.&lt;br /&gt;작업한 결과물을 잘라내서 &lt;code&gt;controller&lt;/code&gt;에 붙여넣어보자&lt;/p&gt;
&lt;pre class=&quot;javascript&quot;&gt;&lt;code&gt;// controllers/cart.js

import Controller from '@ember/controller';

export default class CartController extends Controller {
  get subtotal() {
    return this.model.reduce((acc, item) =&amp;gt; acc + item.price, 0);
  }
  get tax() {
    return 0.09 * this.subtotal;
  }
  get total() {
    return this.subtotal + this.tax;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이처럼 getter를 통해서 각각의 &lt;code&gt;subtotal, tax, total&lt;/code&gt;등을 계산하고 정의했고 이는 화면에 똑같이 출력되는 걸 확인해볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;141&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CwQiW/btsGWJPPWfm/HRvAz2yFX0SpmmKYTVKNFK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CwQiW/btsGWJPPWfm/HRvAz2yFX0SpmmKYTVKNFK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CwQiW/btsGWJPPWfm/HRvAz2yFX0SpmmKYTVKNFK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCwQiW%2FbtsGWJPPWfm%2FHRvAz2yFX0SpmmKYTVKNFK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;667&quot; height=&quot;141&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;141&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 controller와 route를 통해서 우리가 사용하는 render에 여러 가지를 동적으로 집어 넣을 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Ember</category>
      <author>모_아이</author>
      <guid isPermaLink="true">https://mo-i-programmers.tistory.com/80</guid>
      <comments>https://mo-i-programmers.tistory.com/80#entry80comment</comments>
      <pubDate>Thu, 25 Apr 2024 14:56:07 +0900</pubDate>
    </item>
    <item>
      <title>Ember - Tutorial(Routing System)</title>
      <link>https://mo-i-programmers.tistory.com/79</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;업무 진행을 위해 Ember를 처음부터 배워보고 있는데 단순히 보고 Tutorial 을 따라한 것으로는 부족한 것 같아 정리 차 작성해보려 한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 내용은 Ember 공식문서의 Tutorial과 예전 버전이긴 하지만 2022 ember beginners라는 외국 유튜브를 보고 적절히 섞어 작성하였다.&lt;br /&gt;물론 Test에 관련된 부분은 적당히 제거했다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 tutorial에서 만들어지는 웹사이트는 공식문석에 실제로 netlify를 통해 배포되어 있으니 직접 확인해 봐도 된다&lt;br /&gt;&lt;a href=&quot;https://ember-super-rentals.netlify.app/&quot;&gt;Tutorial완성페이지 확인하기&lt;/a&gt;&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;Ember CLI 설치&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 Ember를 쉽게 사용하기 위한 CLI를 먼저 설치 했다&lt;/p&gt;
&lt;pre class=&quot;avrasm&quot;&gt;&lt;code&gt;npm install -g ember-cli&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치가 잘 완료되었는지 확인해보자&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;ember --version&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입력시 이런식으로 출력이 잘 되는지 확인해보자&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;256&quot; data-origin-height=&quot;91&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cqbrQG/btsGUNLwgrX/nPj5JFPPq0bEKRKIrML3NK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cqbrQG/btsGUNLwgrX/nPj5JFPPq0bEKRKIrML3NK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cqbrQG/btsGUNLwgrX/nPj5JFPPq0bEKRKIrML3NK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcqbrQG%2FbtsGUNLwgrX%2FnPj5JFPPq0bEKRKIrML3NK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;256&quot; height=&quot;91&quot; data-origin-width=&quot;256&quot; data-origin-height=&quot;91&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;EmberCLI로 새로운 Ember앱 만들기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;EmberCLI 명령을 사용해서 새 프로젝트를 사용할 수 있다. &lt;code&gt;create react-app&lt;/code&gt;처럼 간단히 만들 수 있는 명령어인데 패턴을 살펴보자면&lt;br /&gt;&lt;code&gt;ember new &amp;lt;project-name&amp;gt;&lt;/code&gt;으로 project-name 부분에 원하는 프로젝트 이름을 작성시 자동적으로 만들어 진다.&lt;br /&gt;이때 공식문서에는 나와있는데로 뒤에 &lt;code&gt;--lane en&lt;/code&gt;을 통해 앱의 기본 언어를 영어롤 설정시키자. 웹 사이트의 접근성이 향상된다&lt;/p&gt;
&lt;pre class=&quot;ada&quot;&gt;&lt;code&gt;ember new test-project --lang en&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 새로 생긴 &lt;code&gt;test-project&lt;/code&gt;폴더에 ember 프로젝트가 생긴 걸 확인할 수 있다.&lt;br /&gt;이후 해당 폴더로 이동하여 &lt;code&gt;npm start&lt;/code&gt;를 실행할 경우 아주 기초적인 웹사이트가 하나 뜨는 걸 확인할 수 있다&lt;br /&gt;주소는 &lt;a href=&quot;http://localhost:4200&quot;&gt;http://localhost:4200&lt;/a&gt; 으로 기본 설정 되어 있다&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;파일 구조 살펴보기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 만들어질 때 파일 구조를 잘 살펴보면 아래와 같이 만들어져 있을 것이다.&lt;/p&gt;
&lt;pre class=&quot;css&quot;&gt;&lt;code&gt;test-project
├── .github
│   └── workflows
│       └── ci.yml
├── app
│   ├── components
│   │   └── .gitkeep
│   ├── controllers
│   │   └── .gitkeep
│   ├── helpers
│   │   └── .gitkeep
│   ├── models
│   │   └── .gitkeep
│   ├── routes
│   │   └── .gitkeep
│   ├── styles
│   │   └── app.css
│   ├── templates
│   │   └── application.hbs
│   ├── app.js
│   ├── index.html
│   └── router.js
├── config
│   ├── ember-cli-update.json
│   ├── environment.js
│   ├── optional-features.json
│   └── targets.js
├── public
│   └── robots.txt
├── tests
│   ├── helpers
│   │   └── index.js
│   ├── integration
│   │   └── .gitkeep
│   ├── unit
│   │   └── .gitkeep
│   ├── index.html
│   └── test-helper.js
├── .editorconfig
├── .ember-cli
├── .eslintcache
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .prettierignore
├── .prettierrc.js
├── .stylelintignore
├── .stylelintrc.js
├── .template-lintrc.js
├── .watchmanconfig
├── README.md
├── ember-cli-build.js
├── package.json
├── package-lock.json
└── testem.js

16 directories, 37 files&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나씩 살펴보자면&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;app 폴더
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본 폴더로 우리가 주로 봐야할 소스 파일들이 존재하는 곳이다(templates,components 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;config 폴더
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;환경 변수가 여기에 설정되는 것과 같은 일부 구성 정보가 포함되어 있다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;public 폴더
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미지나 일부 문서와 같은 모든 정적파일을 저장하는 공용 폴더&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;tests 폴더
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 test파일&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h1&gt;라우팅 시스템&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ember에는 브라우저 URL과 통합되는 라우팅 시스템이 함께 제공된다.&lt;br /&gt;예를 들면 우리가 localhost:4200/cart로 가려 한다면 실제 &lt;code&gt;app/router.js&lt;/code&gt; 내부에서 카트경로를 검색 하기 시작한다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;// ...app/router.js

Router.map(function () {
  this.route('cart');
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 찾고있던 관련 항목을 찾으면 전체 앱이 어떻게 구성되어 있는지 빠르게 확인할 수 있도록 사이트맵 역할을 한다&lt;br /&gt;이때 구동되는게 &lt;code&gt;app/routes/cart.js&lt;/code&gt;와 &lt;code&gt;app/controllers/cart.js&lt;/code&gt;이고 이를 통해 우리가 만든 &lt;code&gt;app/templates/cart.hbs&lt;/code&gt;가 템플릿 렌더링을 시작하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;라우팅에도 여러가지가 있는데&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Application route&lt;/li&gt;
&lt;li&gt;Basic route&lt;/li&gt;
&lt;li&gt;Nested route&lt;/li&gt;
&lt;li&gt;Index route&lt;/li&gt;
&lt;li&gt;Dynamic route&lt;/li&gt;
&lt;li&gt;404 not found route&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등이 있다.&lt;br /&gt;하나씩살펴보자&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Application route&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 ember에는 HCBS의 &lt;code&gt;app/template/application.hbs&lt;/code&gt; 으로 시작되는 기본 애플리케이션 경로가 있다&lt;br /&gt;이는 웹 앱의 시작점이기도 한데 최상위에 존재하는 react로 보자면 main이나 next의 최상위 layout이라고 생각하면 편할 것 같다.&lt;br /&gt;모든 곳에서 쓰이는 부분을 이곳에 적용시키면 모두 적용된다.&lt;br /&gt;Tutorial에선 &lt;code&gt;nav-bar&lt;/code&gt;를 &lt;code&gt;application.hbs&lt;/code&gt;에 적용한 다음 &lt;code&gt;{{outlet}}&lt;/code&gt;을 통해 하위 요소들을 렌더링 시킨다.&lt;br /&gt;마치 react 최상위 요소에서 &lt;code&gt;navbar&lt;/code&gt;컴포넌트를 넣고 아래에 &lt;code&gt;{children}&lt;/code&gt;을 통해 각 페이지들이 렌더링 되도록 하는 것과 같았다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Basic route&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 경로이다.&lt;br /&gt;ember cli를 통해 만들어 낼 수도 있고 직접 파일을 만들 수도 있는데 ember cli가 편한 방법 같기도 하다.&lt;br /&gt;아직 ember에 대해 부족한 부분이 많아 CLI로 만드는게 좋은것인지 직접 만드는 게 좋은것인지 잘 모르겠지만 일단 Tutorial과 beginner 강의가 알려준대로 따라가보자.&lt;/p&gt;
&lt;pre class=&quot;verilog&quot;&gt;&lt;code&gt;ember g route clothes
// 또는
ember generate route clothes&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 작성할 경우 저절로 template에 관련 파일이 생성되고 &lt;code&gt;app/router.js&lt;/code&gt;의 &lt;code&gt;Router.map&lt;/code&gt; 함수 안에도 저절로 입력되어 있을것이다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;//...app/router.js
Router.map(function(){
    this.route('clothes')
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러면 &lt;code&gt;localhost:4200/clothes&lt;/code&gt;로 이동할 경우 &lt;code&gt;app/templates/clothes.hbs&lt;/code&gt; 에 입력한 요소들이 출력되게 된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Nested route&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중첩된 경로로 &lt;code&gt;/clothes/t-shirt&lt;/code&gt;와 같이 경로에 경로로 들어가는 경우이다.&lt;br /&gt;예로 현재 clothes라는 옷 경로를 사용중인데 가장 가까운 하위 카테고리로 티셔츠로 들어가야 한다. 이때는 어떻게 구성을 하고 만들어야 할까??&lt;br /&gt;생각보다 간단한데 이전 &lt;code&gt;clothes&lt;/code&gt;경로를 만들 때처럼 ember CLI를 사용하면 된다&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ember g route clothes/t-shirt&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;clothes는 이미 만들어져 있고 그에 중첩된 경로를 &lt;code&gt;/&lt;/code&gt;를 통해 작성해주면 되는데 이러면 &lt;code&gt;app/tmeplates&lt;/code&gt;폴더에 &lt;code&gt;clothes&lt;/code&gt;라는 폴더가 생성되게 되고 그안에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 &lt;code&gt;router.js&lt;/code&gt;내부 사항도 변경되게 되는데&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;Router.map(function(){
    this.route('clothes',function(){
      this.route('t-shirt')
    })
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처럼 변하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상위 루트의 복사 기능 내에 루트 설정이 추가되는 것이다.(말이 번역본이라 어렵긴 한데 javascript 문법적으로만 보자면 &lt;code&gt;Router&lt;/code&gt;라는 class는 &lt;code&gt;app/router.js&lt;/code&gt; 안에서 선언된 클래스로 &lt;code&gt;EmberRouter&lt;/code&gt;를 상속받고 있다.&lt;br /&gt;그리고 Router 내부에서는 위에서 얘기했던 &lt;code&gt;config&lt;/code&gt;폴더의 환경변수 파일 &lt;code&gt;environment.js&lt;/code&gt;에서 &lt;code&gt;locationtype&lt;/code&gt;과 &lt;code&gt;rootURL&lt;/code&gt;을 덮어 씌우고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 &lt;code&gt;Router&lt;/code&gt;클래스의 &lt;code&gt;map&lt;/code&gt;메서드를 실행시키는데 map 메서드는 &lt;code&gt;RouterDSL&lt;/code&gt;이라는 콜백함수를 받는다. 해당 콜백함수 안에는 &lt;code&gt;route&lt;/code&gt;라는 메서드가 존재하고 &lt;code&gt;route&lt;/code&gt;메서드는 우리가 연결한 &lt;code&gt;url&lt;/code&gt;경로 이름을 첫번째 매개변수로 받고 &lt;code&gt;option&lt;/code&gt;(경로 이름과 파일 이름을 서로 다른걸 사용하고 싶을때)과 또 같은 &lt;code&gt;RouterDSL&lt;/code&gt;을 콜백함수로 가지는 콜백함수를 옵셔널로 실행할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 중첩 라우팅일 경우 route안에 route로 중첩해서 사용하는 방식으로 &lt;code&gt;this.route&lt;/code&gt;를 통해 &lt;code&gt;clothes&lt;/code&gt; 경로를 만든뒤 바로 위에서 설명한 옵셔널한 콜백함수를 다시 작성하여 &lt;code&gt;this.route&lt;/code&gt; &lt;code&gt;t-shirt&lt;/code&gt;를 넣는것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 &lt;code&gt;app/templates/clothes/t-shirt&lt;/code&gt;로 들어가면 &lt;code&gt;t-shirt&lt;/code&gt;에서 작성한 HTML 요소들이 보일 것이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Index route&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 슬래시 마다 기본적으로 보여주는 페이지들이 있는데 이를 index를 통해 관리 할 수 있다.&lt;br /&gt;예를 들어 &lt;code&gt;clothes&lt;/code&gt;라는 경로로 이동했을 때 물론 &lt;code&gt;clothes.hbs&lt;/code&gt;라는 파일 내에서도 작성할 수 있지만 무언가 기본적으로 표시되고 싶은 걸 파일로 나누고 싶다면 &lt;code&gt;index.hbs&lt;/code&gt;를 &lt;code&gt;templates/clothes&lt;/code&gt;폴더안에 생성할 경우 자동적으로 &lt;code&gt;clothes.hbs&lt;/code&gt;의 &lt;code&gt;{{outlet}}&lt;/code&gt;부분에 표시된다.&lt;br /&gt;이때 경로를 &lt;code&gt;clothes/t-shirt&lt;/code&gt;로 들어갈 경우 &lt;code&gt;index.hbs&lt;/code&gt;에서 작성한 요소는 표기되지 않고 &lt;code&gt;t-shirt.hbs&lt;/code&gt;에서 작성한 요소만 표기되며&lt;br /&gt;&lt;code&gt;/clothes&lt;/code&gt;만 들어갈 경우 &lt;code&gt;clothes.hbs&lt;/code&gt;의 &lt;code&gt;{{outlet}}&lt;/code&gt;부분에 &lt;code&gt;index.hbs&lt;/code&gt;의 요소들이 자동적으로 표기되게 된다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 라우팅들에서 표시될 수 있는 기본 값이라고 생각하면 편할 것 같다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Dynamic route&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동적 라우팅으로 동적실행 또는 동적 세그먼트이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주로 사용 되는게 게시물이나 상품의 &lt;code&gt;ID&lt;/code&gt;를 경로에 넣는 것이 있다. &lt;code&gt;item/123&lt;/code&gt;이면 123번의 item을 보여달라는 것과 같다&lt;br /&gt;지금 &lt;code&gt;item/123&lt;/code&gt;의 경우 URL의 마지막 세그먼트 인스턴트인데 한번만들어보자&lt;br /&gt;똑같이 emberCLI를 통해 만든다면&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ember g route item&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;으로 한번 만들어보자 이경우 위에서 &lt;code&gt;router.js&lt;/code&gt; 에 새로 만들어지는 것처럼&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;Router.map(function(){
    this.route('clothes',function(){
      this.route('t-shirt')
    })
    this.route('item')
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가 생성되는데 조금 변경시켜보자&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;Router.map(function(){
    this.route('clothes',function(){
      this.route('t-shirt')
    })
    this.route('item',{path:&quot;/item/:item_id&quot;})
})&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아까 말했던 옵셔널로 주어지는 인자중에 표기와 이름을 다르게 하는 옵션인자를사용해 &lt;code&gt;{path:&quot;/item/:item_id&quot;}&lt;/code&gt;를 통해 실제 url은 &lt;code&gt;item/&lt;/code&gt;과 마지막에 &lt;code&gt;:item_id&lt;/code&gt;가 동적 세그먼트로 들어올수 있도록 한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 실제로 &lt;code&gt;/items/1&lt;/code&gt;으로 경로를 이동해서 실제테스트 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;화면은 잘 뜨는데 그럼 동적 라우팅한 값은 어디서 얻을 수 있을까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;app/routes&lt;/code&gt; 폴더로 이동해보면 emberCLI를 통해 &lt;code&gt;generate&lt;/code&gt;로 만들 때 함께 만들어진 &lt;code&gt;clothes.js&lt;/code&gt;와 &lt;code&gt;item.js&lt;/code&gt;가 자동적으로 만들어져 있을 것이다.&lt;br /&gt;이때 &lt;code&gt;item&lt;/code&gt;에 대해서 동적으로 받는 것에 대한 모델링이 필요함으로&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;import Route from '@ember/routing/route'

export default class ItemRoute extends Route {
    model(params) {
      const {item_id} = params;
      return item_id;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 작성하면 이젠 정상적으로 렌더링 되는 것을 확인할 수 있다&lt;br /&gt;그럼 이 모델에서 &lt;code&gt;return&lt;/code&gt;한 &lt;code&gt;item_id&lt;/code&gt;는 어디서 어떻게 받을 수 있을까? 하고 의문을 가지는데 이는 &lt;code&gt;item.hbs&lt;/code&gt;에서 사용할 수 있다&lt;/p&gt;
&lt;pre class=&quot;django&quot;&gt;&lt;code&gt;// app/templates/item.hbs
&amp;lt;h1&amp;gt; {{this.model}} &amp;lt;/h1&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 작성할 경우 &lt;code&gt;this.model&lt;/code&gt;부분이 아까 &lt;code&gt;return item_id&lt;/code&gt;부분이 되게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 살펴볼때 위에서 설명한 ember의 라우팅 부분에서 마지막 &lt;code&gt;hbs&lt;/code&gt;파일에 도달해서 렌더링 하기 전에 route폴더와 controller 폴더에서 해당되는 js들을 실행시키고 오는 것 같다.&lt;br /&gt;마치 class형 react를 했을 때 처럼 사용되는 함수들은 route폴더 안의 js파일에 메서드들로 작성하고 실행은 hbs에서 &lt;code&gt;{{this.함수명}}&lt;/code&gt;으로 실행시키는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이때 주의할 것으로 실제로 에러를 겪었던 부분인데 이름의 중요성이다. &lt;code&gt;item&lt;/code&gt;이라는 경로를 사용 했는데 templates에서도 &lt;code&gt;item.hbs&lt;/code&gt;여야 하고 routes에서도 &lt;code&gt;item.js&lt;/code&gt; 그리고 실행되는 class도 ItemRoute로 대소문자까지 잘 지켜줘야 실행되는 걸 볼 수 있다.&lt;br /&gt;때문에 직접 만들 경우 오타의 문제가 생길지 모르니 emberCLI를 사용하는 게 더 좋은 것 같다는 생각이 든다&lt;/b&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;404 not found route&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우리가 흔히 보는 에러페이지로 next 등에는 &lt;code&gt;404.js&lt;/code&gt;사용하는 것처럼 사용할 수 있다.&lt;br /&gt;예를 들어 우리가 지정하지 않은 경로로 사용자가 이동 하였을 때 사용자는 개발자가 아니기에 콘솔창을 확인해 보지 않는다.&lt;br /&gt;이로 인해 비어 있는 페이지가 사용자를 반기게 되고 사용자는 페이지가 로딩중인지 에러가 났는지 전혀 알 수가 없는 상태가 된다.&lt;br /&gt;이러면 유저 입장을 전혀 고려하지 않은 설계가 됨으로 이러한 에러 페이지는 필수라고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ebmerCLI를 통해 만들어보자&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;ember g route not-found&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 작성된 경로부분을 수정한다.&lt;/p&gt;
&lt;pre class=&quot;actionscript&quot;&gt;&lt;code&gt;Router.map(function(){
  this.route('clothes',function(){
    this.route('t-shirt');
  });
  this.route('item',{path:&quot;/item/:item_id&quot;});
  this.route('not-found',{path:'/*path'});
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 저장할 경우 허용되지 않은 URL로 들어간다 하더라도 콘솔 오류는 사라지고 &lt;code&gt;application.hbs&lt;/code&gt;에서 작성되어 있는 요소는 그대로 출력되는 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;이후 아래엔 &lt;code&gt;not-found.hbs&lt;/code&gt;에 작성한 에러임을 알리는 요소들이 출력되는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이에 에러 페이지에 다시 되돌아가는 버튼을 만들어 놓는다면 사용자 측면에서 이 페이지가 에러고 다시 돌아가면 되겠다라는걸 명확하게 보여줄 수 있게된다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기까지가 ember에 있는 라우팅 시스템이고 더욱 자세하게 알아본다면 라우팅 시스템 안에 리디렉션과 전환 방지나 재시도 등 여러가지가 있는데 tutorial이 끝나면 또 하나씩 정리 해봐야겠다.&lt;/p&gt;</description>
      <category>Ember</category>
      <author>모_아이</author>
      <guid isPermaLink="true">https://mo-i-programmers.tistory.com/79</guid>
      <comments>https://mo-i-programmers.tistory.com/79#entry79comment</comments>
      <pubDate>Wed, 24 Apr 2024 17:17:40 +0900</pubDate>
    </item>
    <item>
      <title>전역 상태관리 툴 없이 Toast만들기(custom hook으로)</title>
      <link>https://mo-i-programmers.tistory.com/78</link>
      <description>&lt;h1&gt;Toast 만들기&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업무중 atomic component로 사용될 기본 컴포넌트 들을 만들다 Toast의 차례가 왔다&lt;br /&gt;기본적으로 Toast는&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1092&quot; data-origin-height=&quot;416&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/c9jR60/btsGFis0DHO/2eBh3JbfdIl06FMJKExGdk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/c9jR60/btsGFis0DHO/2eBh3JbfdIl06FMJKExGdk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/c9jR60/btsGFis0DHO/2eBh3JbfdIl06FMJKExGdk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fc9jR60%2FbtsGFis0DHO%2F2eBh3JbfdIl06FMJKExGdk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;451&quot; height=&quot;172&quot; data-origin-width=&quot;1092&quot; data-origin-height=&quot;416&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 어떠한 동작을 했거나 실패 등 여러 상황에서 alert창이 아닌 쉽게 말해 서비스 디자인 등과 맞춰 제작된 자체적인 알림창이라 봐도 무방하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React에서 이를 제작할 경우 일단 모든 컴포넌트에서 이 Toast 알림창이 화면에 띄워질 수 있도록 해당 Toast 컴포넌트에 관여 할 수 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;게시판 글이나 뭐 회원가입 버튼이나 각종 여러 군데에서 이 Toast 알림을 띄울 수 있기 때문에 첫번째로 고안한 방법은&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Context API와 같은 전역 상태를 활용해서 가장 최상위 루트에서 감싸주는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럴경우 최상위에서 감싸진 ToastContext에서 useToast라는 함수를 내보내게 되고 이는 Toast가 필요한 컴포넌트에서 이 Toast 알림창을 띄울 수 있는 로직을 사용할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;하지만 문제점이 하나 생기는데..&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 라이브러리들은 이 현상을 해결했다고 하지만 Compound component 디자인 패턴등을 사용하다 보니 ContextAPI를 많이 사용했고 이 Toast 또한 ContextAPI로 전역 상태를 감쌌더니 Toast가 뜰때마다 모든 컴포넌트들이 리렌더링 되는 일이 발생했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때문에 Recoil이나 redux, zustand와 같은 전역 상태관리 라이브러리들이 생겨났다고도 읽었는데 확실히 사용자가 버튼을 연속해서 누를 경우도 있고 그 때는 엄청난 리렌더링이 일어나 성능적으로 매우 부적절하다 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 고치기 위해선 모든 컴포넌트에 memo를 씌운다면 가능하겠지만... &lt;b&gt;합리적이지 못하다&lt;/b&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;어떻게 해결했나?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 Toast들을 사용하고 있는 UI 라이브러리들을 뒤졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;import해서 사용하고 있는 라이브러리들은 내부적으로 보기가 까다로웠지만 그 중 shadcn 이라는 라이브러리를 찾았는데 manual 부분과 직점 install하면 사용되는 코드를 볼 수가 있어 직접 코드를 분석해봤다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://ui.shadcn.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://ui.shadcn.com/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1713330021841&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;shadcn/ui&quot; data-og-description=&quot;Beautifully designed components that you can copy and paste into your apps. Accessible. Customizable. Open Source.&quot; data-og-host=&quot;ui.shadcn.com&quot; data-og-source-url=&quot;https://ui.shadcn.com/&quot; data-og-url=&quot;https://ui.shadcn.com&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/fQu0C/hyVPWVkxis/Gj31fWzGoN7dK7LRrekyi0/img.png?width=1280&amp;amp;height=727&amp;amp;face=0_0_1280_727&quot;&gt;&lt;a href=&quot;https://ui.shadcn.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://ui.shadcn.com/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/fQu0C/hyVPWVkxis/Gj31fWzGoN7dK7LRrekyi0/img.png?width=1280&amp;amp;height=727&amp;amp;face=0_0_1280_727');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;shadcn/ui&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Beautifully designed components that you can copy and paste into your apps. Accessible. Customizable. Open Source.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;ui.shadcn.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;shadcn 페이지에 들어가서 docs에 Toast를 살펴보면 알겠지만 shadcn의 Toast를 설치하면 약 3개의 파일을 다운받게 되는데&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1455&quot; data-origin-height=&quot;1247&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/4QIlR/btsGIeJiEJD/DJUL0xnp1qGc6RD8wfsgN0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/4QIlR/btsGIeJiEJD/DJUL0xnp1qGc6RD8wfsgN0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/4QIlR/btsGIeJiEJD/DJUL0xnp1qGc6RD8wfsgN0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F4QIlR%2FbtsGIeJiEJD%2FDJUL0xnp1qGc6RD8wfsgN0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1455&quot; height=&quot;1247&quot; data-origin-width=&quot;1455&quot; data-origin-height=&quot;1247&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 3개의 파일이 만들어지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각 간단히 살펴보면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Toast는 띄워지는 알림창의 component이고&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp; Toaster는 알림창이 띄워지기 위한 레이아웃이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- use-toast는 toast를 각 컴포넌트에서 사용하기 위한 toast 라는 함수와 Toaster에서 관리하기 위한 useToast를 관리하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 shadcn에서 사용되는 방법을 간단히 소개하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. &amp;lt;Toaster/&amp;gt;라는 toast 레이아웃을 최상위 루트에서 잡아놓는다. 이는 Toast 알림창이 하나만 뜨게 할것인지 여러개 뜨게 할 수 있을것인지는 직접 CSS와 Toast 상태값을 조절하면 된다.(일단 Shadcn은 Toast를 하나만 뜨도록 만들어놨다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. &amp;lt;Toaster /&amp;gt; 내부에서 useToast를 사용하 toasts 즉, 뜨게할 알림창들을 배열로 가져온 다음 map을 돌려 &amp;lt;Toast/&amp;gt;로 화면에 표시한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. use-toast에는 export를 통해 Toaster에 보낼 toasts와 각 컴포넌트에서 사용된 toast함수를 내보내는데 이 toast 함수를 통해 각 컴포넌트에서&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1713330547863&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;toast({
	title:'test',
    description:'test toast'
    })&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 함수식을 버튼의 onClieck 과 같은 곳에 실행되도록 하여 use-toast에서의 toast함수를 실행되도록 한다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 이걸 봤을 때 이게 그래서 어떻게 전역적으로 상태가 관리된다는 것인가? 궁금할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;보통 우리가 자주 보는 custom hook은 각 컴포넌트에서 useCustomHook과 같이 사용할 때 각 컴포넌트 별로 상태들이 만들어지고 각각은 독립적인 상태가 된다&amp;nbsp;&lt;/b&gt;는게 보통 많이 접했을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;shadcn은 독특하게 hook이 불려질 때 해당 hook파일 자체적으로 변수를 가지고 있도록 파일 즉 해당 모듈에서의 전역변수를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 그 변수에 hook이 불릴때마다 만들어지는 state들을 관리할 수 있는 setState들을 배열로 집어넣고 toast라는 함수가 발동할 때마다 이 setState들을 순환하여 모든 state들을 변경할 수 있게 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 해당 hook이 사용된 컴포넌트들만 리렌더링 됨으로 전역적인 리렌더링을 방지할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style5&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;의문점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그대로 사용하기엔 의문점이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;toast알림창들의 layout으로 잡고 있는 Toaster에서만 리렌더링 되면 안되나? 왜 굳이 배열에 넣고 반복문을 돌리지? 라고 생각이 들 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다시 생각해보면 shadcn은 공통적으로 사용할 수 있게 라이브러리로 나온 상황이라 사용자가 &amp;lt;Toaster/&amp;gt;를 루트 한군데에서만 사용한다는 보장이 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇기에 &amp;lt;Toaster/&amp;gt;를 컴포넌트별로 분할시켜 사용한다면 각각의 상태를 다 업데이트 해야 하기 때문에 배열로 만들었다 생각하고 내가 만들 Toaster는 루트에서 한번만 관리할 것이기 때문에 쓸데없는 부분은 삭제시키고 새로 만들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ToastLayout.tsx&lt;/p&gt;
&lt;pre id=&quot;code_1713331106055&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { memo } from 'react';
import { useToast } from '../../hooks/useToast';
import { cn } from '../../lib/utils';

const ToastLayout = () =&amp;gt; {
  const [toastArr, closeToast] = useToast();

  return (
    &amp;lt;div className='fixed bottom-2 right-2 z-100 flex flex-col'&amp;gt;
      {toastArr.length !== 0 &amp;amp;&amp;amp;
        toastArr.map((toast) =&amp;gt; (
          &amp;lt;Toast key={toast.id} closeToast={closeToast} {...toast} /&amp;gt;
        ))}
    &amp;lt;/div&amp;gt;
  );
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;useToaste.tsx&lt;/p&gt;
&lt;pre id=&quot;code_1713331037442&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import { Dispatch, SetStateAction, useEffect, useState } from 'react';

type ToastType = {
  id: number;
  content: string;
  variant?: ColorVariants;
  soft?: boolean;
  className?: string;
};
const TOASTCLOSEDELAY = 3000;
let toastListener: Dispatch&amp;lt;SetStateAction&amp;lt;ToastType[]&amp;gt;&amp;gt;;
let toastId = 0;

type ToastFunctionType = Omit&amp;lt;ToastType, 'id'&amp;gt;;

const openToast = ({
  content,
  variant,
  soft,
  className,
}: ToastFunctionType) =&amp;gt; {
  toastId = (toastId + 1) % Number.MAX_SAFE_INTEGER;
  toastListener((prev) =&amp;gt; [
    ...prev,
    { id: toastId, content, variant, soft, className },
  ]);
  setTimeout(() =&amp;gt; {
    toastListener((prev) =&amp;gt; prev.slice(1));
  }, TOASTCLOSEDELAY);
};

const useToast = () =&amp;gt; {
  const [toastArr, setToastArr] = useState&amp;lt;ToastType[]&amp;gt;([]);

  const closeToast = (id: number) =&amp;gt;
    setToastArr((prev) =&amp;gt; prev.filter((toast) =&amp;gt; toast.id !== id));

  useEffect(() =&amp;gt; {
    toastListener = setToastArr;
  }, []);

  return [toastArr, closeToast] as const;
};

export { useToast, openToast };&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 테스트 용으로 만든 Toast이며 &amp;lt;ToasteLayout/&amp;gt;이라는 Toast들이 뜰 공간을 잡아놓고 useToast를 통해 상태를 하나 잡아놓는다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 openToast라는 함수를 통해 각 컴포넌트에서 사용하고 openToast가 실행될 경우 useToast란 파일에서 잡아놓은 toastLitener변수가 Toaster가 가지고 있는 toastArr이란 상태를 업데이트 할 수 있기 때문에 Toaster가 리렌더링 되며 toast가 화면에 나타나게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 적절히 사용되는 Toast에도 memo를 걸어줄 경우 Toast알림창이 뜰경우 리렌더링이 굉장히 최소화 되는 부분을 확인해 볼 수 있다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 toast알림창이 뜰때 화면 우측 하단의 ToastLayout과 새롭게 만들어지고 사라지는 toast들만 리렌더링 되는지 체크해보자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코드에 정답이 없다지만 이렇게 useToast안에 모듈 전역변수를 두는 게 좋은 방식인가? 는 의문이 들기도 하여 추후에 더 좋은 방법이 있다면 실행해 볼만 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아니면 아싸리 전역 상태 관리 라이브러리에서 Toast를 관리하는 것도 훨씬 좋은 방안이라 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>업무</category>
      <author>모_아이</author>
      <guid isPermaLink="true">https://mo-i-programmers.tistory.com/78</guid>
      <comments>https://mo-i-programmers.tistory.com/78#entry78comment</comments>
      <pubDate>Wed, 17 Apr 2024 14:24:17 +0900</pubDate>
    </item>
  </channel>
</rss>