AdonisJS 알아보기

MMaximilian Schwarzmüller
컴퓨터/소프트웨어창업/스타트업AI/미래기술

Transcript

00:00:00감사합니다.
00:00:30네, 안녕하세요 여러분. 오늘 Adonis.js에 관한 스트림에 오신 것을 환영합니다. 재미있을 것 같네요. 제가 한 번도 써본 적 없는 흥미로운 프레임워크이자 라이브러리입니다. 항상 써보고 싶었기에 이번 스트림에서 한번 다뤄보려고 합니다.
00:01:00잠시만 정리 좀 할게요.
00:01:07모두 잘 지내시길 바랍니다. 함께해주셔서 감사합니다.
00:01:16자, 그럼 시작해 보죠. 네, 여러분 안녕하세요. 오늘 저는 아도니스(Adonis)에 대해 좀 더 깊이 알아보고 탐구해 보려고 합니다. 그냥 재미삼아서 하는 건데요, 아도니스JS는 다른 프레임워크와는 꽤나 다른 자바스크립트 프레임워크거든요.
00:01:38물론, 지금은 2019년이 아니죠. 그러니 요즘 자바스크립트 프레임워크가 얼마나 중요한지 논할 수도 있겠죠, 그렇죠? 매일 새로운 프레임워크가 쏟아져 나오던 프레임워크 전쟁의 시대는 끝났으니까요.
00:01:54오늘날은 온통 AI 이야기뿐이죠. 하지만 요즘 다들 AI만 이야기하니까, 저는 반대로 AI와는 전혀 관련 없는 주제로 라이브 스트리밍을 해보면 어떨까 싶었습니다.
00:02:08아도니스는 꽤 흥미로운 프레임워크이자 라이브러리입니다. 말씀드렸듯이 직접 써본 적은 없지만, 어떤 것인지는 알고 있죠. 꽤 오랜 기간 느슨하게나마 지켜봐 왔거든요.
00:02:20결국 아도니스는 '자바스크립트용 라라벨(Laravel)'이라고 생각합니다. 제가 이해하고 있는 아도니스는 그렇습니다. 풀스택 프레임워크이고, 리액트 프레임워크들이 풀스택이 되기 전부터 이미 존재했죠.
00:02:40그러니까... 넥스트JS(Next.js)가 나오기 전에 출시되었는지까지는 확실하지 않지만, 넥스트JS가 엄청난 인기를 얻기 전부터 있었다고 할 수 있겠네요.
00:02:51네, 아도니스는 정말 흥미로운 기능들을 많이 내장하고 있어요. 오늘 제 계획은 공식 소개 가이드를 따라가면서 무엇이 들어있는지 살펴보고, 직접 이것저것 해보면서 어떻게 작동하는지 알아보는 겁니다.
00:03:10아, 구글 AI 플랜의 새로운 컴퓨터 기반 할당량에 대한 댓글이 있네요. 관련 내용을 읽어본 것 같긴 한데, 자세히 살펴보지는 못해서 딱히 코멘트할 게 없네요.
00:03:24와, 이건 꽤 큰 변화네요. 자, 봅시다. 아도니스가 뭐죠? '백엔드 우선(back-end first)' 타입 세이프 프레임워크군요. 백엔드 우선이긴 하지만, 풀스택 기능도 갖추고 있다는 건 알고 있습니다.
00:03:37라라벨을 아신다면 아시겠지만, 아도니스로도 뷰(view)를 렌더링할 수 있죠.
00:03:42Node.js와 타입스크립트로 웹 애플리케이션을 구축하는 데 필요한 핵심 빌딩 블록을 제공하며, 외부 라이브러리 없이도 완전한 백엔드를 작성하고 유지 관리할 수 있게 해줍니다.
00:03:52아까 말씀드렸듯이, 라라벨처럼 단순히 리액트 컴포넌트를 서버에서 렌더링하는 기능을 주는 Next.js 같은 풀스택 프레임워크 그 이상입니다.
00:04:06다소 거친 요약일 수도 있지만, 그게 Next.js의 핵심 아이디어라고 할 수 있죠.
00:04:12라우팅 등도 지원하지만, Adonis는 라라벨처럼 더 많은 것을 제공합니다.
00:04:16별도의 외부 라이브러리 없이도 인증 기능이 내장되어 있습니다.
00:04:21파일 업로드, 캐싱, 속도 제한 등을 지원하며 훨씬 더 많은 기능을 제공하죠.
00:04:26고유의 ORM, 즉 객체 관계 매핑도 내장되어 있습니다.
00:04:33SQL 데이터베이스 등을 다루는 자체 래퍼죠.
00:04:35정말 강력하고 유능합니다.
00:04:38결국 풀스택 앱을 만들고 싶을 때 다른 외부 의존성을 많이 추가하지 않아도 Adonis.js 하나만으로 충분하다는 것이 큰 장점이죠.
00:04:47물론, 특히 요즘처럼 공급망 공격이 많은 시기에 제한된 의존성을 가진다는 건 정말 흥미로운 점입니다.
00:04:55물론 Adonis 자체가 공격당하면 위험하긴 하지만, 필요한 외부 의존성이 줄어든다는 것은 장점입니다.
00:05:03어이, Daki! 당신의 유명한 리액트 강의를 마치는 중이에요.
00:05:07순수 타입스크립트나 리액트 플러스 타입스크립트 다음에는 무엇을 배우는 게 좋을까요?
00:05:11네, 리액트와 타입스크립트를 같이 배우는 건 좋은 생각입니다.
00:05:12Next.js도 좋은 생각이죠.
00:05:14아니면 리액트 다음으로 리액트 네이티브를 배워 모바일 앱 개발로 넘어가는 것도 합리적인 다음 단계라고 생각합니다.
00:05:20다들 괜찮은 다음 단계가 될 거예요.
00:05:24더 긴말할 것 없이, 텍스트를 다 읽기보다는 바로 시작해 보죠.
00:05:28경로를 정합시다.
00:05:31여기서 배울 것들입니다.
00:05:33사실 시작해 보고 싶어요.
00:05:36한 가지 짚고 넘어갈 건, 세 가지 접근 방식입니다.
00:05:40Adonis.js는 프론트엔드를 구축하는 세 가지 주요 접근 방식을 지원합니다.
00:05:44오, 이건 중요하겠네요.
00:05:47각 방식은 뷰 레이어를 다루는 서로 다른 관점을 나타냅니다.
00:05:52하이퍼미디어.
00:05:52하이퍼미디어 애플리케이션은 서버에서 완전한 HTML 페이지를 생성하여 브라우저로 보냅니다.
00:05:57템플릿 엔진을 사용하여 인터페이스를 구축하죠.
00:06:00Adonis.js에는 자체 템플릿 엔진인 'Edge'가 내장되어 있습니다.
00:06:05그리고 Alpine.js와 같은 경량 자바스크립트 라이브러리를 사용하여 상호작용을 추가하는 방식이죠.
00:06:10필요하다면 HTMX나 Poly를 사용합니다.
00:06:12저는 이 방식을 사용해 보려고 합니다.
00:06:16물론, 라라벨 세계에서 온 Inertia.js를 사용하여 리액트를 프론트엔드로 사용할 수도 있습니다.
00:06:25이건 라라벨 진영에서 나온 건데,
00:06:28프론트엔드 싱글 페이지 애플리케이션과
00:06:32풀스택 애플리케이션의 백엔드를 이어주는 다리 역할을 합니다.
00:06:38그리고 당연히 REST API만 구축하는 백엔드로 사용할 수도 있고요.
00:06:46같은 컨트롤러에서 세 가지 다른 리턴 방식이 가능합니다.
00:06:49좋아요, 한번 보죠.
00:06:50Adonis에 내장된 라우터를 사용해 경로를 등록할 수 있습니다.
00:06:56여기가 컨트롤러네요.
00:07:02거기서 viewRender를 호출해 미리 정의한 뷰를 렌더링할 수 있습니다.
00:07:06이 특별한 구문은 Adonis에 포함된 Edge 템플릿 언어입니다.
00:07:16대안으로 Inertia를 사용할 수도 있는데, 역시 컨트롤러가 필요합니다.
00:07:20컨트롤러 안에서 뷰에 렌더를 호출하고 props를 전달할 수 있죠.
00:07:28네, 그 안으로 데이터를 넘기는 거죠.
00:07:30그리고 여기에 자바스크립트, 리액트 코드를 작성할 수 있죠.
00:07:34제가 알기로는 서버 사이드 렌더링이 될 겁니다.
00:07:37기본적으로 리액트 컴포넌트 내에서 데이터를 가져올 필요가 없죠.
00:07:42모든 게 알아서 처리됩니다.
00:07:44그것도 좋네요.
00:07:45하지만 제가 뭘 쓸지는 좀 더 봐야겠네요.
00:07:47둘 중 하나겠죠. 작은 풀스택 앱을 만들어보고 싶거든요.
00:07:50어떻게 될지 보죠.
00:07:51저는 아마 Edge 템플릿 엔진으로 시작해서 예전 방식대로 다중 페이지 애플리케이션을 만들 것 같아요.
00:07:57서버 사이드에서 HTML을 렌더링하는 방식이죠.
00:07:59모든 애플리케이션이 클라이언트 사이드 리액트가 필요한 건 아니니까요.
00:08:03그걸 이해하는 게 정말 중요하다고 생각해요.
00:08:05무엇을 만드느냐에 따라 다른 거죠.
00:08:07모든 앱이 엄청나게 반응형이고 인터랙티브한 프론트엔드가 필요한 건 아닙니다.
00:08:12예를 들어 블로그를 만든다면, 굳이 필요 없겠죠.
00:08:16더 복잡한 애플리케이션이라도 필요 없는 경우가 많을 겁니다.
00:08:20채팅창 내용을 잠깐 확인할게요.
00:08:22안녕하세요, Max.
00:08:23AI 엔지니어링 코스요?
00:08:24언젠가는 할 수 있겠지만, 지금은 상황이 너무 빨리 변해서
00:08:28강의를 내놓는 순간 이미 과거의 것이 될 것 같아요.
00:08:32그런 건 원치 않아요.
00:08:32그래서 오늘은 아닙니다.
00:08:35차라리 소프트웨어 기초 같은 강의를 먼저 만들고 싶네요.
00:08:39하지만 언젠가는 제가 AI를 어떻게 활용하는지 꼭 공유하고 싶습니다.
00:08:44지금처럼 매주 변하는 게 아니라, 어느 정도 안정된 시점에 말이죠.
00:08:48안녕하세요, Max.
00:08:48Adonis는 10년 넘게 유지되어 온 것으로 알고 있는데,
00:08:51틈새시장이라 사업적인 리스크가 있다고 생각하시나요?
00:08:55네, 정말 좋은 질문입니다.
00:08:57아주 오랫동안 유지되어 왔다는 건 알고 있습니다.
00:09:0110년이나 된 줄은 몰랐네요.
00:09:02하지만 정말 긴 시간이죠.
00:09:05Adonis 팀의 규모가 얼마나 큰지는 잘 모릅니다.
00:09:09지금 아주 잘 유지되고 있다는 건 확실해요.
00:09:13X(트위터)에서 메인 관리자, 즉 Adonis 프로젝트 소유자가 활발히 활동하는 걸 자주 보거든요.
00:09:20전 세계적으로 사용되지 않는 프로젝트에는 항상 어느 정도 위험이 따르기 마련이죠.
00:09:30하지만 그렇게 오랫동안 유지되어 왔다는 사실 자체가 관리자의 헌신을 증명하는 게 아닐까 합니다.
00:09:38그래도 Vercel의 지원을 받는 Next.js 같은 것이 장기적으로는 더 확실하게 유지될 가능성이 높다고 볼 수는 있겠죠.
00:09:50하지만 절대라는 건 없으니까요.
00:09:52Adonis는 Next.js가 갖지 못한 확실한 장점들이 있기도 하고요.
00:09:57하지만 분명 유효한 우려 사항입니다.
00:10:00라이브 방송 좋네요.
00:10:01프로그래밍 언어나 프레임워크에 대한 당신의 접근 방식이 좋고, Adonis는 처음 보는데 꽤 흥미롭네요.
00:10:05오늘 어떤 걸 얻게 될지 궁금합니다.
00:10:07저도 궁금하네요.
00:10:08참여해 주셔서 감사합니다.
00:10:09재미있을 거예요.
00:10:10화면 영역 강조 표시에는 어떤 도구를 쓰시나요?
00:10:12DemoPro라는 앱이에요.
00:10:15Mac 전용 애플리케이션입니다.
00:10:17구매한 지 아마 꽤 오래됐을 거예요.
00:10:21정말 훌륭하죠.
00:10:21화면에 직접 그릴 수 있게 해주는 기능들을 제공하거든요.
00:10:25네.
00:10:27아직도 왜 자바스크립트 커뮤니티에서 이걸 받아들이지 않는지 모르겠어요.
00:10:30의견이 분명한(opinionated) 프레임워크는 정말 좋잖아요.
00:10:33사람들이 Svelte나 Vue는 좋아하는데, 왜 Adonis는 아닐까요?
00:10:35글쎄요.
00:10:36저도 항상 궁금했던 점이에요.
00:10:37처음 Adonis를 봤던 게 한 7~8년 전쯤이었던 것 같아요.
00:10:44그때도 왜 더 유명해지지 않을까 생각했거든요.
00:10:48개인적으로 깊이 파볼 시간이 없었네요.
00:10:52그리고 결코 엄청나게 인기가 많았던 적도 없고요.
00:10:54그러다 보니 강의 주제로 삼을 기회도 없었죠.
00:10:59잘 모르겠네요.
00:11:00정말 괜찮아 보이는데 말이죠.
00:11:02어쨌든 시작해보면 얼마나 좋은지 알 수 있겠죠.
00:11:07자, 시작해 봅시다.
00:11:09네.
00:11:10물론 Node가 필요하겠죠. 새 애플리케이션을 생성하기 위해 npm create를 씁니다.
00:11:16복사할게요.
00:11:19작은 폴더를 이미 만들었어요.
00:11:21저는 npm 대신 더 빠르고 보안이 좋은 bun을 사용할 겁니다.
00:11:25이 폴더에서 생성할 수 있을까요?
00:11:27될까요?
00:11:29네.
00:11:29좋아요.
00:11:30이제 선택해야겠네요.
00:11:31하이퍼미디어 앱을 사용할까요?
00:11:33템플릿 엔진, 리액트, 뷰 앱 중에 말이죠.
00:11:38저는 하이퍼미디어로 가겠습니다.
00:11:39스타터 키트를 다운로드하고 의존성을 설치하며 데이터베이스를 마이그레이션 중입니다.
00:11:45기본 스타터 애플리케이션을 제공해주나 보네요.
00:11:53명령어 실패.
00:11:55Node ace migration run.
00:11:58어라.
00:11:59시작이 좋지 않네요.
00:12:01bun을 써서 그런가?
00:12:04아니면...
00:12:05최신 Node 버전인 Node 26을 사용해서 그럴 수도 있습니다.
00:12:12Node로 실행하려고 해서 문제가 생겼을지도 모르겠네요.
00:12:20한번 보죠.
00:12:21이렇게 실행할 수 있을까?
00:12:24좋아.
00:12:25오류, 예외가 발생했네요.
00:12:28이런, 맙소사.
00:12:29라이브 스트림에서 뭐 좀 시도하려고 하면 꼭 문제가 생기더라고요.
00:12:35Adonis bin console TS에서 startenv 모듈을 찾을 수 없대요.
00:12:45확인해 봅시다.
00:12:47Node 때문인가?
00:12:48더 낮은 Node 버전을 사용해 볼게요.
00:13:01아니네요.
00:13:02다른 오류가 떴어요.
00:13:08바인딩 파일을 찾을 수 없다고 하네요.
00:13:14허.
00:13:14어라.
00:13:15Node 버전 때문일 수도 있으니, 폴더 내용을 싹 다 지우고 다시 시도해 볼게요.
00:13:24이번엔 NPM으로 다시 만들어보고, 제발 공급망 공격 같은 건 당하지 않기를 바랄 뿐입니다.
00:13:34요즘은 NPM 쓰기가 정말 겁나네요.
00:13:38저는 BUN이나 PNPM을 선호하거든요.
00:13:42자.
00:13:43다시 해보죠.
00:13:44잘 되길 바라며.
00:13:52이 정도면 딱히 잘못될 것도 없어야 하는데 말이죠.
00:14:11명령어 실패.
00:14:12시작이 정말 엉망이네요.
00:14:19이거 알려진 문제인가요?
00:14:23혹시 다들 아는 건가?
00:14:27딱히 그런 것 같진 않네요.
00:14:36좋아.
00:14:37한번 보죠.
00:14:38AI한테 물어보는 것도 방법이죠.
00:14:43해결할 수 있을지 보죠.
00:14:46요즘 제가 가장 좋아하는 코딩 에이전트인 Pi를 실행할게요.
00:14:57이 프로젝트에서 Adonis를 시작하려고 해요.
00:15:02실행했죠.
00:15:03명령어가 뭐였더라?
00:15:04NPM create this.
00:15:09알겠어요.
00:15:10NPM create.
00:15:13Adonis.js 최신 버전.
00:15:17마이그레이션 실행 중에 실패했어요.
00:15:22수동으로 실행해도 안 되고요.
00:15:24오류 로그를 붙여넣어서 결과를 한번 보죠.
00:15:29좋은 아침.
00:15:30아니면 저녁인가요?
00:15:32여긴 오후네요.
00:15:34AI가 도와줄 수 있을지 보죠.
00:15:38그 에러 메시지를 처리할 수 있다면요.
00:15:41Adonis.js가 뭐죠?
00:15:42Adonis.js는 자바스크립트 프레임워크입니다.
00:15:44풀스택 프레임워크죠.
00:15:47자바스크립트계의 라라벨 같은 겁니다.
00:15:50라라벨을 아신다면요.
00:15:51즉, '배터리 포함(batteries-included)' 프레임워크입니다.
00:15:53Next와는 다르죠.
00:15:54Next.js나 주로 서버 라우팅과 렌더링에 집중하는 Tanstack Start와는 달라요.
00:15:59대신 인증 기능이 내장되어 있습니다.
00:16:01자체 ORM도 함께 제공되죠.
00:16:03그게 핵심입니다.
00:16:04지금은 초기 설정 고정 과정에서 몇 가지 문제에 직면해 있습니다.
00:16:10아, 그렇군요.
00:16:11그러니까, 그건 아니군요.
00:16:14알겠습니다.
00:16:15이해가 돼요.
00:16:16Adonis는 아니군요.
00:16:18아마도요.
00:16:19그런데 문제는 이 설정 명령어가 라이프사이클 스크립트를 실행하려고 했던 것 같아요.
00:16:28그러니까 의존성에 연결된 스크립트죠.
00:16:31공급망 공격 때문에 제가 ignore-scripts를 true로 설정해 놨거든요.
00:16:35Bun도 제가 알기로 기본적으로는 실행하지 않고요.
00:16:38그래서 실행이 필요한 스크립트가 있었던 모양입니다.
00:16:45그래서 AI가 이걸 일시적으로 비활성화했습니다.
00:16:49이제 마이그레이션을 실행했고요.
00:16:54좋아요.
00:16:57이제 초기 설정 단계에서 누락된 것이 없기를 바랍니다.
00:17:05확인해 봐야겠죠.
00:17:07이제 개발 서버를 실행해 볼 수 있으니까요.
00:17:15자, 해보죠.
00:17:19네, 여기서 저 브라우저는 쓰고 싶지 않아요.
00:17:22어디 봅시다.
00:17:24좋아요.
00:17:25화면에 뭔가 떴네요.
00:17:31좀 이상해 보이는데.
00:17:37원래 이렇게 보여야 하나요?
00:17:41허.
00:17:43알겠습니다.
00:17:45어쨌든.
00:17:50자, 어디 보자.
00:17:51여기서 가입할 수 있네요.
00:18:05이제 로그인했습니다.
00:18:07로그아웃도 가능하고요.
00:18:08이 모든 게 기본적으로 구현되어 있네요.
00:18:12안 생겨도 될 문제들이 항상 튀어나오죠.
00:18:15네.
00:18:16정말 전형적이에요.
00:18:17라이브 스트리밍에서 새로운 기술을 다뤄보려고 할 때마다 벌써 세 번째 이러는 것 같네요.
00:18:22결국 실패하고 말죠.
00:18:23그래도요.
00:18:23이제 잘 작동합니다.
00:18:25이제 아무 문제 없이 돌아가는 것 같아요.
00:18:31이 temp 폴더는 좀 신경 쓰이네요.
00:18:33여기에 제 프로덕션 데이터베이스가 저장되나요?
00:18:36아니면 개발 데이터베이스인가요?
00:18:37스키마요.
00:18:38사용자들.
00:18:39네.
00:18:39오케이.
00:18:40SQLite를 사용하고 있네요. 가볍고 단순해서 마음에 듭니다.
00:18:45여기 무엇이 있는지 잠시 후에 살펴보겠습니다.
00:18:48Udemy는 강사를 팔로우해서 새 콘텐츠가 나올 때 알 수 있는 기능을 추가해야 해요.
00:18:53그럼요.
00:18:54다른 강사들과 함께 Udemy에 추가하면 좋을 만한 쿨한 기능들에 대해 오랫동안 이야기해 왔거든요.
00:19:01하지만 뭐,
00:19:02그들은 별로 원하지 않는 것 같더라고요.
00:19:05AI를 쓰기 전에 스스로 문제를 해결하려고 노력하시는 모습이 좋네요.
00:19:08저는 첫 번째로 AI에게 복사해서 붙여넣기 할 것 같거든요.
00:19:11그럼 전 망한 건가요?
00:19:12보시다시피 제가 못 했을 때 AI가 해결해주었죠.
00:19:15하지만 저도 이 스트리밍이 디버깅이 아닌 Adonis에 대한 내용이기를 바랐기에 전체 에러 스택을 다 훑고 싶진 않았습니다.
00:19:25그래서 특히 긴 에러 메시지를 파싱하고 그 이유를 따져볼 때 AI가 정말 유용합니다.
00:19:31AI에게 넘기기 전에 최소한 잠깐이라도 에러 메시지를 직접 살펴보는 게 의미가 있다고 생각해요.
00:19:38왜냐하면 때로는 아주 쉽게 해결될 문제일 수 있거든요.
00:19:42포트 문제 같은 간단한 것일 수도 있고요.
00:19:45이번 경우는 아니었지만 다른 문제라면요.
00:19:47그리고 무엇이 진행 중이고 어떤 부분이 깨지는지에 대해 최소한의 아이디어를 가지고 있는 것이 결코 나쁘지 않으니까요.
00:19:58어쨌든, 무엇이 있는지 살펴봅시다.
00:20:01이 프로젝트에는 당연히 package.json 파일이 있고요.
00:20:08이 프로젝트를 구성하는 파일들에 대한 매핑 정보가 있습니다.
00:20:14그리고 Adonis 관련 의존성들도 있고요.
00:20:19Vine은 뭐죠?
00:20:24유효성 검사 라이브러리군요, 알겠습니다.
00:20:25유효성 검사 라이브러리가 있고, better-sqlite3, edge.js, 그게 템플릿 언어군요.
00:20:34Luxon은요?
00:20:39그건 또 뭐죠?
00:20:41날짜와 시간을 다루는 라이브러리군요.
00:20:44알겠어요. 그리고 assertion, 테스트 러너 타입, Alpine도 있네요.
00:20:54Alpine은 기본적으로 가벼운 클라이언트 측 자바스크립트 코드를 추가하기 위한 자바스크립트 라이브러리라서, 바닐라 자바스크립트를 직접 짤 필요가 없으면서도 React처럼 무거운 라이브러리를 쓸 필요도 없죠.
00:21:05아, Adonis rc.ts는 설정 파일인 것 같네요. 실험적 플래그, 명령어들, ace 명령어 목록이 있네요.
00:21:15네, 마지막으로 라라벨로 작업한 지 꽤 오래됐네요.
00:21:202014년, 15년, 16년쯤 라라벨 개발을 많이 했었는데, 기억하기로 라라벨은 Adonis처럼 기능이 아주 많았고 자신만의 명령어를 등록할 수 있게 해줬습니다.
00:21:38라라벨에서는 artisan 명령어였던 것 같은데, 여기서는 Node로 실행하는 ace 명령어인 것 같네요.
00:21:49즉, Adonis가 제공하는 도구이고, 자신만의 하위 명령어를 등록할 수 있는 것 같습니다. 그리고 파일들을 직접 가져와서 등록할 수도 있고요.
00:22:05예를 들어 myFile.ts 같은 곳에서 나만의 유틸리티 명령어를 등록하는 거죠.
00:22:15서비스 공급자(Service Provider)를 가져오고 등록하는 걸 보니, 웹 서버가 시작될 때 의존성 주입과 관련하여 다양한 서비스가 작동하도록 등록하는 부분 같네요.
00:22:26여긴 무엇이 있나요?
00:22:28여긴 무엇이 있나요?
00:22:28데이터베이스 제공자, 인증 제공자가 있네요. 여러 기능들을 위한 공급자들이죠.
00:22:34Preload도 있고, 그렇군요. 여기가 설정 파일이네요. 이해했습니다.
00:22:40또 뭐가 있죠?
00:22:41포트 등을 설정하는 env 파일이 있고, 로그 레벨, 앱 키도 있네요.
00:22:50비밀번호 해싱이나 다른 용도로 쓰이는지 확실하진 않지만요.
00:23:01세션 드라이버 쿠키도 있네요. 인증을 위해 쿠키를 사용하는군요. 데이터베이스 설정도 있고요.
00:23:12Git 무시.
00:23:14temp 폴더, 네.
00:23:16테스트 폴더, 유닛 테스트가 설정되어 있네요.
00:23:23Resources 폴더, 라라벨이랑 비슷하네요.
00:23:27Laravel에도 클라이언트 측 리소스를 담은 resources 폴더가 있었죠. 여기도 마찬가지로 클라이언트 측 CSS와, 말씀드렸던 Alpine을 사용한 JavaScript 코드가 들어 있습니다.
00:23:40views는 기본적으로 프론트엔드 페이지나, 페이지로 렌더링될 템플릿, 그리고 모든 컴포넌트가 들어있는 것 같네요.
00:23:49하지만 이 템플릿 시스템이 어떻게 작동하는지는 아직 잘 모르겠습니다.
00:23:53이 Edge 템플릿 언어를 위한 확장 프로그램을 설치할 수 있는지 빨리 확인해 봐야겠네요. 공급망 공격은 당하지 않길 바라야죠.
00:24:02Edge 템플릿 지원, 여기 있네요. Adonis.js에서 만든 공식 Edge 템플릿 구문 강조 프로그램입니다.
00:24:09설치해 보죠.
00:24:12아, 제발 해킹은 당하지 말아야 할 텐데.
00:24:16좋네요, 이제 좀 낫네요.
00:24:18이제 구문 강조가 적용되었습니다.
00:24:20템플릿 언어의 개념은, 예전에는 다른 JavaScript 프레임워크에서도 템플릿 언어를 사용하는 게 꽤 흔했죠.
00:24:32기본적으로 서버에서 HTML 페이지를 렌더링하고, 완성된 HTML 코드를 클라이언트로 보내는 방식이죠.
00:24:39더 편리하고 동적으로 만들기 위해 HTML 파일을 여러 템플릿으로 구성하고, 그 템플릿 안의 플레이스홀더를 동적 값으로 교체하는 겁니다.
00:24:51예를 들어, pages 폴더 안에 있는 이 홈페이지는 레이아웃에 기반하고 있네요.
00:25:02따로 지정하지 않으면 어디엔가 등록된 기본 레이아웃을 사용하겠죠.
00:25:09이 부분은 그 레이아웃에 주입되는 거고요.
00:25:12레이아웃에는 플레이스홀더들이 있겠네요.
00:25:14어디 봅시다, components 폴더에 레이아웃이 정의되어 있군요.
00:25:20여기가 레이아웃 파일입니다.
00:25:22HTML 기본 뼈대가 보입니다.
00:25:24body 태그 안에 main 슬롯이 있네요.
00:25:29여기가 실제 페이지 콘텐츠가 렌더링될 자리를 정의하는 Edge 템플릿 언어 구문인 것 같네요.
00:25:39이 언어가 어떻게 작동하는지 아직은 잘 모르겠지만요.
00:25:42읽으면서 이해해보려 합니다.
00:25:44이름을 지정할 수 있는 것 같기도 하고요.
00:25:47혹은 예약어일지도 모르죠.
00:25:49잘 모르겠습니다.
00:25:50async네요.
00:25:51흥미롭군요.
00:25:52await하고 있고요.
00:25:53여기가 렌더링될 때까지 레이아웃 렌더링을 끝내지 않는 거죠.
00:25:57slots.main이군요.
00:25:59확실히 예약어인 것 같습니다.
00:26:01그 외에도 라라벨에서 봤던 파셜(partials) 개념도 있네요. 레이아웃뿐만 아니라 다른 컴포넌트들을 가져와서 페이지에 삽입하는 건 흔한 방식이죠.
00:26:18헤더 파셜, 플래시 알림 파셜 등이 보입니다. 화면에 보여줄 경고 메시지용이겠죠.
00:26:26예를 들어, 헤더 파셜은 partials 폴더의 header 파일에서 찾을 수 있습니다.
00:26:33자, 헤더를 열어보죠.
00:26:35링크를 렌더링하기 위한 템플릿 구문이 보이네요.
00:26:40링크가 이렇게 렌더링되는 건 동적이기 때문입니다.
00:26:43내부 링크라면 라우트 등록 정보에 의존하겠죠.
00:26:47홈 라우트로 이동하는 '홈'이라는 텍스트의 링크를 설정하고 있네요.
00:26:52확인할 수 있습니다.
00:26:54어디서 볼 수 있죠?
00:26:55하단에서 볼 수 있나요?
00:26:58아니면,
00:26:59이거네요.
00:26:59SVG 파일이군요.
00:27:01이게 그 SVG인 것 같네요.
00:27:04홈이라는 이름의 대체 텍스트가 있고요.
00:27:06클릭하면 홈 라우트로 이동하죠.
00:27:13사용자가 로그인했는지 여부에 따른 조건부 렌더링도 있네요.
00:27:19말이 되네요.
00:27:20아직 헤더 안에 있습니다.
00:27:23오케이.
00:27:23여기에 @vite라고 되어 있네요.
00:27:26그러니까, 프런트엔드 에셋을 번들링하고 관리하는 데 Vite를 사용하고 있는 것 같네요.
00:27:35그리고 최적화되고 번들링된 app.js와 app.css 파일을 여기로 주입하는 것 같고요.
00:27:45더 구체적으로는, resources 폴더 아래의 JS와 CSS에서 그걸 찾을 수 있겠네요.
00:27:49그러니까 저 JavaScript를 CSS 파일로 옮기는 거죠.
00:27:53처리되는 건지는 잘 모르겠지만요.
00:27:57알겠어요.
00:27:58메인 개발 서버를 시작하면 Vite 서버도 자동으로 실행되겠죠.
00:28:05좋습니다.
00:28:06이해가 돼요.
00:28:06모든 걸 다 이해한 건 아니지만요.
00:28:08스택 관련 내용은 디버깅용인 것 같네요.
00:28:11잘 모르겠어요.
00:28:12더 많은 컴포넌트가 있군요.
00:28:15그리고 네.
00:28:16더 파봐야겠습니다.
00:28:17정확히 무슨 일이 일어나는 건지 말이죠.
00:28:19나중에 천천히 하죠.
00:28:21채팅으로 돌아가겠습니다.
00:28:22질문들이 좀 있네요.
00:28:24최신 LLM 모델들은 사용하기 더 비싸지고 더 이상 보조금도 안 나오는데,
00:28:28오픈 소스나 자체 호스팅 모델이 더 중요해질까요?
00:28:31그 결과로 개발자들이 DevOps를 배워야 할까요?
00:28:35앞으로 오픈 모델이 더 중요해질 거라고 봅니다.
00:28:42하지만 직접 실행하려면 역시 비용이 들겠죠.
00:28:45매우 고사양 장비를 갖춰야 하거나,
00:28:48예를 들어 1만 달러짜리 Mac Studio 같은 거요.
00:28:52아니면 다시 대여해야 하니까요.
00:28:53그러니까 비용은 항상 들기 마련입니다.
00:28:56하지만 프론티어 모델 가격이 비싸짐에 따라 오픈 모델이 흥미로운 대안이 될 수 있겠죠.
00:29:03무엇을 하려는지에 따라 다르겠지만, 아주 뛰어난 오픈 모델이 아직은 많지 않거든요.
00:29:10하지만 결국 그렇게 되겠죠.
00:29:12그렇다고 DevOps를 꼭 배워야 한다고는 생각 안 합니다.
00:29:16그냥 그 분야 자체에 관심이 있다면 배우시는 게 좋겠죠.
00:29:19하지만 오픈 모델을 직접 배포하고 운영하는 건 하드웨어가 있거나,
00:29:25VPS를 빌려서 한 번씩 해보는 정도라면 모를까요.
00:29:28한 번 제대로 설정하는 법만 익히면 됩니다.
00:29:32일반적으로 DevOps를 깊게 배울 필요는 없다고 봅니다.
00:29:36아, 네, 제 Flutter 강의를 들어주셔서 감사합니다.
00:29:41멋진 언급을 해주신 Don Solid 님, 감사합니다.
00:29:47그리고 이 도구는 왜 사용해야 하나요? 그냥 AI 서비스를 직접 쓰는 게 낫지 않나요?
00:29:51어떤 도구를 말씀하시는지 잘 모르겠네요.
00:29:54혹시 새 강의를 Udemy에 올릴 계획이 있으신가요?
00:29:57아직도 Udemy 강의를 하시나요?
00:30:01가끔 하긴 합니다.
00:30:02하지만 지금 Udemy의 상황이 아주 좋은 편은 아닙니다.
00:30:07Coursera 인수 건 등 앞으로 어떻게 돌아갈지 지켜봐야 할 것 같아요.
00:30:14그리고 콘텐츠 보안 정책(CSP)과 관련해서는, 제가 생각하기에 해당 레이아웃으로 가서,
00:30:22그냥 head 섹션에 필요한 내용을 추가하면 될 겁니다.
00:30:25어차피 결국은 HTML이니까 그냥 거기에 추가하시면 됩니다.
00:30:32그러면 작동할 거예요.
00:30:34물론 저는 이제 막 Adonis를 시작한 단계지만요.
00:30:42좋아요, 그럼 이건 됐고.
00:30:44프론트엔드나 리소스 쪽은 일단 넘어가도록 하죠.
00:30:48start 폴더를 살펴봅시다.
00:30:50나머지를 다 둘러보지 않았으니 이게 서버 측 부분일 것 같네요.
00:30:56더 있거든요.
00:30:57마이그레이션이 포함된 database 폴더가 있습니다.
00:31:00다른 프레임워크와 마찬가지로 여기서도 마이그레이션 파일을 통해 데이터베이스 테이블을 정의하는 방식입니다.
00:31:08이런 형식을 사용하는 것 같군요.
00:31:12마이그레이션 파일을 자동으로 생성해주는 명령어들도 당연히 있을 겁니다.
00:31:17그리고 해당 마이그레이션을 실행해서 데이터베이스에 적용하고 테이블과 스키마를 설계하게 되죠.
00:31:24스키마 TS 파일이 있네요.
00:31:28데이터베이스 테이블을 클래스 형태로 설정하는 것 같습니다.
00:31:33아마 특정 명령어를 통해 프로그래밍 방식으로 마이그레이션을 생성하겠죠.
00:31:40설정 관련 파일들이 굉장히 많네요.
00:31:43이것저것 설정할 수 있는 것들이 많군요.
00:31:50나중에 필요할 때 확인해보면 될 것 같습니다.
00:31:53bin 폴더의 server TS 파일은 HTTP 서버 진입점입니다.
00:31:57서버 TS 파일은 Adonis.js HTTP 서버를 시작하기 위한 진입점입니다.
00:32:02이 파일을 직접 실행하거나 serve 명령어를 사용할 수 있습니다.
00:32:05개발 서버를 시작할 때 serve 명령어를 사용했으니 그때 이 파일이 실행된 거죠.
00:32:13여기서 뭘 하는지 볼까요?
00:32:18임포터와 이그나이터(igniter)가 있네요.
00:32:23기본적으로 서버를 부팅하고 포트에서 대기하는 역할을 합니다.
00:32:27분명 어딘가에 라우트를 등록하고 있을 겁니다.
00:32:34console 파일은 Adonis.js 명령줄 프레임워크를 부팅하기 위한 진입점입니다.
00:32:38사용자 정의 명령어를 등록하거나 내장 명령어를 ace 명령어로 실행할 때 사용하겠죠.
00:32:45그다음 앱 폴더를 보면,
00:32:46시작 관련 요소들을 여기서 등록하는 것 같습니다.
00:32:49start 폴더가 흥미롭네요.
00:32:51start 폴더 안에 routes TS 파일이 있습니다.
00:32:54이 파일에서 애플리케이션의 라우트를 명확하게 등록하네요.
00:33:00슬래시 경로처럼 말이죠.
00:33:02도메인 뒤에 아무것도 없는 경로죠.
00:33:04홈 페이지를 렌더링합니다.
00:33:06리소스 뷰 폴더의 경로를 참조하고 있네요.
00:33:12그러니까 resources/views 안쪽입니다.
00:33:15render pages home이라고 하면 여기 pages 폴더로 이동하게 되는 거죠.
00:33:20그 안에서 홈 페이지를 렌더링하고요.
00:33:23본질적으로 여기서 일어나는 과정은 이렇습니다.
00:33:28라우트를 그룹화할 수도 있네요.
00:33:31왜 그룹화를 할까요?
00:33:33공통 미들웨어를 적용하기 위해서겠죠?
00:33:35게스트 미들웨어 같은 것들요.
00:33:38무슨 기능을 하든 간에요.
00:33:40가입 및 로그인 라우트가 여기 있네요.
00:33:44인증되지 않은 사용자만 접근할 수 있도록 하는 미들웨어인 것 같습니다.
00:33:49반대로 인증된 사용자만 접근할 수 있는 라우트를 보호하기 위해 auth 미들웨어를 쓰기도 하죠.
00:33:56로그아웃 라우트처럼 인증된 상태여야만 의미가 있는 경우 말입니다.
00:34:00라우트를 등록하는 방법은 직접 뷰를 렌더링하거나, Laravel에서도 그랬듯이 컨트롤러와 메서드를 라우트에 연결하는 것이 더 일반적인 철학으로 보입니다.
00:34:21가입 경로를 보면, get과 post 라우트가 있고 같은 컨트롤러 파일을 사용하지만, 생성(create)과 저장(store)을 담당하는 각각 다른 컨트롤러 메서드를 타겟으로 합니다.
00:34:35컨트롤러 파일을 보면 adonis.js 서버 쪽에 위치해 있네요.
00:34:47네, 그런데 이건 동적으로 생성된 것 아닌가요?
00:34:51우리가 직접 만들었나요?
00:34:53안 만들었나?
00:34:55아니요, 컨트롤러를 여기서 등록하는군요.
00:34:59동적으로 생성되는 걸로 생각됩니다.
00:35:01자, 여기 NewAccountController가 있네요.
00:35:04클래스가 이렇게 정의되어 있습니다.
00:35:08그리고 그 안에 create 메서드와 store 메서드가 있네요.
00:35:12결국 이 메서드들이 해당 작업을 담당하는 거죠.
00:35:15이것도 어떻게든 동적이거나 자동으로 생성된다고 봅니다.
00:35:21create 메서드(get 라우트)에서는 단순히 뷰를 렌더링하고요.
00:35:25이론적으로는 이걸 사용하지 않고 직접 get 요청에 처리할 수도 있지 않을까 싶은데,
00:35:41가능한지 모르겠네요. 컨트롤러를 거치지 않고 바로 렌더링할 수는 없나요?
00:35:46무조건 컨트롤러를 써야 하나요?
00:35:50그런 것 같네요.
00:35:51하지만 어차피 렌더링 방식은 똑같네요.
00:35:56Post 라우트에서는 내장 유효성 검사기를 사용하여 사용자 입력을 검증합니다.
00:36:04그리고 User 모델을 통해 사용자 인스턴스를 생성하죠. 그건 나중에 더 다룰게요.
00:36:11그 후 사용자를 웹 애플리케이션에 인증시킵니다.
00:36:15세션 및 쿠키 기반 인증을 하는 것 같군요.
00:36:18그런 다음 홈 라우트로 리다이렉트합니다.
00:36:21한번 볼까요?
00:36:24Models 폴더가 있네요.
00:36:26거기에 User 모델이 정의되어 있습니다.
00:36:28앞서 데이터베이스 테이블과 스키마에 대해 언급했었죠.
00:36:40아마 모델만 정의하면 나머지는 자동으로 생성되는 것 같지만, 좀 더 살펴보죠.
00:36:47결국 여기에서 애플리케이션에서 사용할 User를 정의하는 겁니다.
00:36:53그리고 Adonis에서 제공하는 기능을 확장하고요.
00:36:57여기 getter인 initials가 있네요.
00:37:03이름과 성을 결합해서 전체 이름을 가져오는 간단한 헬퍼 함수입니다.
00:37:10계산된 값을 이런 식으로 만들 수 있다는 걸 보여주네요.
00:37:15오케이, 알겠습니다.
00:37:17사용자 검증(Validators) 부분이군요.
00:37:19이름은 생소하지만 Wine이라는 라이브러리를 사용해서 이메일 형식이 올바른지, 길이는 적절한지 등을 검증합니다.
00:37:31잠시만요.
00:37:36가입 시 특정 값을 강제하기 위해 어디서든 재사용할 수 있는 검증 로직을 만들 수 있는 것 같군요.
00:37:49Middleware의 AuthMiddleware는 핸들 메서드를 가진 클래스인데, 여기서 인증을 확인하고 통과하면 next를 반환해서 요청한 라우트로 접근하게 해줍니다.
00:38:07인증에 실패하면 에러를 던져서 다음 이동을 막는 것 같네요.
00:38:14GuestMiddleware는 정반대일 겁니다.
00:38:21사용자가 이미 인증되었는지 확인하고 인증되었다면 리다이렉트합니다.
00:38:28인증되지 않은 경우에만 원래 가려던 경로에 접근하도록 허용하죠.
00:38:32오케이.
00:38:33Exceptions 폴더에서는 우리만의 커스텀 에러를 정의할 수 있습니다.
00:38:38오케이.
00:38:40오케이.
00:38:42자.
00:38:45가까운 시일 내에 계획 중인 새로운 강의는 무엇인가요?
00:38:52아마 BUN(번) 관련 강의를 할 수도 있겠네요.
00:38:55이 강의가 만족스러울지 아직 고민 중입니다.
00:38:58이미 녹화한 내용은 있지만 품질이나 방향성이 마음에 드는지 확인하고 싶어서요.
00:39:06소프트웨어 공학 기초나 시스템 디자인 관련 강의도 올해 안에 만들고 싶습니다.
00:39:12에이전트 AI 강의는 잘 팔리긴 하겠지만 앞서 말했듯이 고민이 있어요.
00:39:20제가 에이전트 엔지니어링을 어떻게 하는지, AI와 어떻게 작업하는지 공유하는 강의를 꼭 만들고 싶습니다.
00:39:26문제는 이 분야가 너무 빠르게 변한다는 거죠.
00:39:30지금 당장 강의를 만드는 건 별로 유용하지 않을 것 같아요.
00:39:33조금 더 안정화되었다고 느껴지면 에이전트 엔지니어링이나 AI 엔지니어링 관련 강의를 꼭 만들고 싶습니다.
00:39:42puck.js처럼 보이네요.
00:39:44네.
00:39:45왜 2026년에 누군가 그걸 쓰려고 할까요?
00:39:48아무도 Vue(뷰)를 쓰지 않을 것 같아요.
00:39:51SPA가 대세니까요.
00:39:53저는 동의하지 않습니다.
00:39:56우리는 싱글 페이지 애플리케이션과 모든 곳에서의 React(리액트)에 너무 익숙해져 있어요.
00:40:01리액트는 훌륭하죠.
00:40:02그리고 AI가 리액트를 사랑한다는 건 분명한 사실이기도 합니다.
00:40:07하지만 Adonis는 일반적으로 꽤 니치한 편이죠.
00:40:10그렇다고 모든 곳에 리액트를 써야 한다고 생각하는 건 잘못된 가정이라고 봐요.
00:40:17서버에서 템플릿을 렌더링하는 것만으로도 충분할 때가 많거든요.
00:40:23그걸로 충분합니다.
00:40:24리액트를 추가하면 복잡성과 번들 사이즈만 더 커질 수 있고요.
00:40:30최근 몇 주간 발생한 보안 취약점들을 생각해보세요.
00:40:35익숙하다는 이유만으로 불필요한 것을 도입하지 않는 데에도 분명 가치가 있습니다.
00:40:41우리는 모든 곳에 리액트를 사용하는 데 너무 익숙해져서, 당연히 써야 할 표준 옵션으로 받아들이고 있죠.
00:40:47전 그렇지 않다고 생각합니다.
00:40:49물론 동의해요.
00:40:50이건 확실히 좀 구식처럼 보이긴 하죠.
00:40:53하지만 제가 말했듯이 저도 수년 전에 이런 식으로 페이지를 작성했었습니다.
00:41:00그래도 오늘날 틀린 방식이라고 하기는 어렵습니다.
00:41:03분명 이례적이고 네이티브처럼 느껴지거나 상식적인 방법처럼 보이진 않죠.
00:41:12하지만 충분히 의미가 있을 수 있다고 봅니다.
00:41:14모든 것을 고려했을 때, 이전에도 말했듯이 Adonis는 다양한 뷰 렌더링 방식을 지원합니다.
00:41:23Inertia.js를 다리로 사용하여 React를 프론트엔드로 지원하기도 하고요.
00:41:29그러니 이런 뷰 기반 접근 방식 없이도 Adonis.js 풀스택 애플리케이션을 완벽하게 구축할 수 있습니다.
00:41:38하지만 저는 어떻게 보이는지 확인하고 싶어서 먼저 살펴본 것뿐입니다.
00:41:43Hano랑 좀 비슷하게 생겼네요.
00:41:45네, Hano가 훨씬 가볍죠, 안 그런가요?
00:41:48Hano는 인증이나 ORM 같은 기능들이 내장되어 있지 않으니까요.
00:41:56Adonis.js는 어떠신가요?
00:42:00흥미롭긴 한데, 전에는 전혀 사용해본 적이 없어서요.
00:42:03오늘 처음 탐색해보는 중입니다.
00:42:05이제 막 시작했거든요.
00:42:07하나의 프레임워크 안에 모두 포함되어 있다는 아이디어가 맘에 들어요.
00:42:13요즘 같은 공급망 공격이 많은 시대엔 특히 더요.
00:42:16게다가 자바스크립트 생태계에서는 너무 복잡한 솔루션을 택하는 경향이 있다고 생각합니다.
00:42:21수십 개의 라이브러리를 가져와 엮는데, 그건 공급망 공격 위험 외에도,
00:42:26모든 라이브러리가 항상 잘 관리되지는 않는다는 문제가 있죠.
00:42:31그러니 모든 게 다 갖춰진 '배터리 포함' 프레임워크가 분명한 장점이 있다고 생각합니다.
00:42:39기본 지식을 갖춘 컴퓨터 공학 학생들에게 어떻게 프로그래밍을 배우라고 추천하시나요?
00:42:44다음 단계는 무엇인가요?
00:42:45프로젝트를 만드는 것일까요, AI를 탐색하는 것일까요?
00:42:46저는 학습을 위해 AI를 활용할 것 같아요.
00:42:48AI의 도움을 받아 프로젝트도 만들겠지만, AI만 전적으로 의존하진 않을 겁니다. 왜냐하면,
00:42:53그렇게 하면 아무것도 배울 수 없으니까요.
00:42:54소프트웨어 공학 기초를 배우려고 노력할 것 같습니다.
00:42:57코드를 직접 손으로 한 줄 한 줄 작성해보고요.
00:43:01AI가 생성한 모든 코드를 검토하고 제대로 이해하려고 노력할 겁니다.
00:43:05비판적으로 질문도 던지면서요.
00:43:07코드에 대해 AI와 논의하는 용도로 쓸 수도 있겠죠.
00:43:10하지만 솔직히 말씀드리면 저도 요즘 AI를 활용해 어떻게 가장 잘 가르치고 배울 수 있는지 알아가는 중입니다.
00:43:15배우고 있는 단계죠.
00:43:20AI에게 물어보면서 배울 수 있게 프로젝트를 어떻게 만드느냐는 거죠?
00:43:27네.
00:43:27AI를 스파링 파트너로 활용할 것 같습니다.
00:43:32알겠습니다.
00:43:32자, 한번 보죠.
00:43:34아까 이야기한 대안들도 확인해보고 싶거든요.
00:43:38그러고 나서 코드를 수정해보죠.
00:43:40일단 대안을 살펴볼 겁니다.
00:43:42만약 제가...
00:43:47다시 만들어 보겠습니다.
00:43:49처음부터 다시 하고 싶거든요.
00:43:51다시 만들게요.
00:43:52서버를 멈추고,
00:43:53React 키트를 사용하는 대안적인 접근 방식을 시도해봅니다.
00:44:01이제 또 충돌할지도 모르겠네요.
00:44:08잠시만 확인해보죠.
00:44:15제대로 작동하게 하려면 어떤 명령어를 실행해야 하더라?
00:44:19Permanent.
00:44:20영구적인 건 원하지 않아요.
00:44:27이게 아마 될 겁니다.
00:44:32봅시다.
00:44:36봅시다.
00:44:37웁스.
00:44:38그건 하려던 게 아닌데.
00:44:39점 하나면 충분합니다.
00:44:42좋아요.
00:44:44잘 작동하는지 봅시다.
00:44:55바지에 슈바르츠뮐러(Schwarzmullering)를 하다니.
00:44:57좋은 건지 나쁜 건지 모르겠네요.
00:45:09제 Flutter 강의를 처음부터 직접 디자인했냐고요?
00:45:11네.
00:45:12모두 제 작업이죠.
00:45:14자, 이제 다시 시작해 봅시다.
00:45:21똑같이 보이는지 확인해 보죠.
00:45:24네.
00:45:25같은 앱이네요.
00:45:26하지만 당연히 파일 구조는 달라졌을 겁니다.
00:45:29어디 봅시다.
00:45:32앱 폴더.
00:45:36이제 transformer(변환기)가 생겼네요.
00:45:38하지만 그건 그냥 백엔드 관련 작업인 것 같아요.
00:45:42아니군요.
00:45:42서버에서 클라이언트로 데이터를 전송하기 위한 것 같아요.
00:45:46프론트엔드로 보내는 거죠, 한번 봅시다.
00:45:48bin, config, database, inertia 폴더가 있네요.
00:45:53오케이.
00:45:53Inertia가 이제 프론트엔드 폴더인 것 같네요.
00:45:56CSS.
00:45:57네.
00:45:57알겠습니다.
00:45:58여기 뭐가 있죠?
00:45:59app.tsx 파일이 있네요.
00:46:11뭔지는 정확히 모르겠지만, 프론트엔드를 하이드레이션하고 백엔드와 연결하기 위한 것 같네요.
00:46:18Inertia 앱을 생성하고,
00:46:20페이지 제목을 설정하는 것 같네요.
00:46:22앱.
00:46:23좋아요.
00:46:24스크립트 설정 파일이 어디 있죠? 에러가 표시되는데, 그냥 deprecated(구식)라서 그런 거니 괜찮아요.
00:46:29그건 괜찮습니다.
00:46:32서버 사이드 렌더링 TSX 파일이네요.
00:46:34그건 서버 사이드 렌더링 진입점이고, 이건 클라이언트 진입점이네요.
00:46:39오케이.
00:46:39Inertia가 어떻게 작동하는지, 이 2시간짜리 영상이 뭔지 전혀 모르겠네요.
00:46:46홈페이지가 여기 있네요.
00:46:47이제 일반적인 React 컴포넌트가 되었군요.
00:46:51별다른 건 없어요.
00:46:52레이아웃 같은 게 있나요?
00:46:53네, 레이아웃이 있네요.
00:46:54Default TSX 파일이요.
00:46:55이건 어디서 설정하죠?
00:46:56앱에서 설정하는 건가요?
00:46:59페이지 컴포넌트를 확인해 보죠.
00:47:00Pages 폴더를 보면,
00:47:03레이아웃, 레이아웃, 레이아웃, 레이아웃.
00:47:05레이아웃이 불러와지고 있네요.
00:47:07좋아요.
00:47:08그런 식으로 작동하는군요.
00:47:10여기에 레이아웃이 있어요.
00:47:12그리고 기본 레이아웃도 가져왔네요.
00:47:16좋아요.
00:47:16오, useEffect가 있네요.
00:47:19그건 금지인데.
00:47:24좋아요.
00:47:25네.
00:47:26정말 그냥 React네요.
00:47:27우리가 알다시피 Link 컴포넌트는 Inertia에서 옵니다.
00:47:30아니면 Adonis.js의 Inertia 패키지에서 오겠죠.
00:47:35알겠습니다.
00:47:37제가 이해한 바로는, 어디 봅시다.
00:47:47네.
00:47:47Children은 당연히...
00:47:51좋아요.
00:47:52좋아요.
00:47:53좋아요.
00:47:54흥미롭네요.
00:47:54Children은 항상 React에서 그렇듯 태그 사이의 컴포넌트죠.
00:48:05그런데 Children도 User 속성을 가진 Props 객체를 가지고 있네요.
00:48:13아마도 어딘가 컨트롤러에서 채워지겠죠.
00:48:20먼저, 새로운 세션, 회원가입의 새로운 계정 컨트롤러나 Inertia 렌더링 쪽이요.
00:48:29그런데 거기선 Props를 전혀 전달하지 않네요.
00:48:32세션 컨트롤러는 어디 있죠?
00:48:35이게 어디서 채워지는 걸까요?
00:48:36왜냐하면 여기에 Transformer라는 게 있는데, 전 이게 서버 사이드 데이터를 클라이언트 사이드로 변환하는 건 줄 알았거든요.
00:48:46확실하지 않네요.
00:48:50이것, 이 리소스, 무엇이든 반환해라.
00:48:56아마 데이터베이스에서 데이터를 가져오기 위한 용도일 수도 있겠네요.
00:48:59잘 모르겠네요.
00:49:01오케이.
00:49:02자, 이렇게 설정이 되었군요.
00:49:03공식 가이드를 계속 봅시다.
00:49:05저는 직접 코드베이스를 먼저 파고드는 걸 정말 좋아해요.
00:49:08그리고 가이드를 따르기 전에 스스로 이해해 보려고 노력하죠.
00:49:12그렇게 하면 더 많이 배우는 것 같거든요.
00:49:16요즘 누가 코드를 읽나요, 그렇죠?
00:49:18그러니 스트리밍을 통해 하는 건 어떨까요?
00:49:22농담입니다.
00:49:23저는 코드를 읽어요.
00:49:26폴더 구조.
00:49:28좋아요.
00:49:28방금 폴더 구조를 살펴봤어요.
00:49:31앱 폴더.
00:49:32앱 디렉토리는 애플리케이션의 도메인 로직을 위한 코드를 구성합니다.
00:49:36예를 들어, 컨트롤러, 모델, 메일 같은 것들이죠.
00:49:39네, 라라벨처럼 메일 전송도 도와주는데, 물론 매우 편리하죠.
00:49:44bin 디렉토리는 Adonis.js 애플리케이션을 실행하기 위한 진입점 파일들을 포함합니다.
00:49:50앱이 어떻게 부팅되는지 커스텀하려는 게 아니라면 보통 이 파일들은 수정할 필요가 없어요.
00:49:56좋아요.
00:49:56config 폴더에는 모든 애플리케이션 및 타사 설정 파일들이 들어 있습니다.
00:50:01이 디렉토리 안에 애플리케이션에 로컬한 설정을 저장할 수도 있어요.
00:50:05네, config 폴더에 보면 이미 데이터베이스 같은 것들이 있는데, 우린 SQLite를 사용하죠.
00:50:13아마도 Postgres 데이터베이스 같은 걸로 연결할 수도 있을 거라 생각해요.
00:50:20클라이언트를 선택할 수 있죠.
00:50:21그러니 여기서 Bun의 SQLite 클라이언트 같은 걸 사용할 수도 있겠네요.
00:50:24잘 모르겠지만요.
00:50:25데이터베이스 경로, temp 경로를 정의하죠.
00:50:28자, temp 폴더 여기 있고, 마이그레이션, 그 외에 뭐가 있죠?
00:50:34config, inertia, 서버 사이드 렌더링, 서버 사이드 렌더링 모드 토글.
00:50:39네, 서버 사이드 렌더링을 원하니 true로 바꿉시다.
00:50:47로거도 있네요.
00:50:48그러니까 말이 되네요.
00:50:50빨리 확인해 보고 싶어요.
00:50:52만약 이걸 false로 설정하면, 페이지 소스를 봤을 때, 데이터가 전달된 앱 div가 있겠죠.
00:51:06하지만 클라이언트 사이드에서 렌더링되기 때문에 주 HTML 내용은 없을 겁니다.
00:51:10클라이언트 사이드 렌더링을 수행하는 일부 스크립트를 가져오겠죠.
00:51:15검색 엔진 최적화(SEO)에는 좋지 않아요.
00:51:18그래서 이걸 true로 설정하면, 보시다시피 훨씬 더 많은 내용이 보이죠?
00:51:26내용이 모두 인라인 처리되어 읽기는 조금 어렵지만, 실제 HTML 내용이 존재하네요.
00:51:31그러니 true로 유지할게요.
00:51:34오케이, 데이터베이스.
00:51:35데이터베이스 디렉토리는 데이터베이스 계층 관련 아티팩트들을 보관합니다.
00:51:39기본적으로 Adonis.js는 Lucid ORM을 사용하죠.
00:51:43ORM의 아이디어는 데이터베이스 테이블을 코드 상의 클래스, 모델로 표현하는 것입니다.
00:51:51그럼 기초가 되는 스키마와 마이그레이션이 자동으로 생성되죠.
00:51:57네, 이 프레임워크를 탐구하는 건 이번이 처음이라, 어떻게 작동하는지 지금 알아가는 중이에요.
00:52:03데이터베이스를 바꾼다고 이 폴더를 재구성할 필요는 없습니다. 마이그레이션, 버전, 스키마 변경, 초기 관리자 유저 같은 시드 데이터를 원하면 시더까지 다 있죠.
00:52:15오케이, providers.
00:52:16Providers 디렉토리는 애플리케이션에서 사용되는 서비스 프로바이더를 저장하는 데 사용됩니다.
00:52:20서비스 프로바이더가 뭐죠?
00:52:22서비스 프로바이더.
00:52:22이 가이드에서 다루네요.
00:52:24음, 전부 다 읽지는 않을 거예요.
00:52:27서비스 공급자는 애플리케이션 시작 및 종료 시 특정 시점에 실행되는 수명 주기 후크를 가진 자바스크립트 클래스입니다.
00:52:35이를 통해 제어 역전(IoC) 컨테이너에 바인딩을 등록하고, 매크로를 사용하여 프레임워크 클래스를 확장하고, 정확한 시점에 초기화를 수행하며, 종료 시 리소스를 정리할 수 있습니다.
00:52:53그래서 내장 프로바이더들이 몇 개 있는 거군요.
00:52:58어디 있죠?
00:53:02아까 봤는데.
00:53:04내장 프로바이더들이 어디 있죠?
00:53:09Providers.
00:53:09여기 API 프로바이더가 있네요.
00:53:12API 응답을 위한 커스텀 시리얼라이저(직렬화기)군요.
00:53:17좋아요, 여기 데이터를 직렬화하는 프로바이더가 있네요.
00:53:22인증을 돕거나, 기본 미들웨어를 등록하는 더 많은 내장 프로바이더들이 있을 거라고 가정해요.
00:53:30그럴 것 같네요.
00:53:33저장은 하지 맙시다.
00:53:35오케이, public 디렉토리에는 원시 정적 자산이 들어 있습니다.
00:53:39이 폴더를 놓쳤었나?
00:53:40Public.
00:53:46저는 public 폴더가 없네요.
00:53:48하지만 public 자산은 따로 없나 봐요.
00:53:51최적화되지 않은 원시 자산들을 가지기 위해 public 폴더를 추가할 순 있겠죠.
00:54:05Adonis는 일자리가 거의 안 보이던데.
00:54:07왜 수요도 없는 걸 탐구하나요?
00:54:10그냥 재미로?
00:54:10네, 만약 일자리를 찾고 있다면 Adonis.js는...
00:54:15오, 정말 어려운 질문이네요.
00:54:17제 말은, 네, 일자리를 찾는다면 Adonis.js는 도움이 안 돼요.
00:54:22하지만 첫째로, 대안을 탐구하는 건 항상 시야를 넓혀주죠.
00:54:27여기서 배운 새로운 개념들이 다른 기술 스택을 사용하는 다른 애플리케이션에도 적용될 수 있어요.
00:54:33재미로 한다는 건 매우 타당한 이유죠.
00:54:36그리고 만약 일자리를 찾는 게 아니라, 자신만의 SaaS나 사업을 구축하려는 목적이라면 당연히 타당한 선택지가 될 수 있죠.
00:54:48물론 일자리를 찾는다면, 지역에 따라 매우 틈새시장일 순 있어요.
00:54:52그리고 솔직히 말해서, 너무 틈새라 일자리를 찾기는 매우 어려울 겁니다.
00:54:58하지만 일자리가 있는 지역에 있다면 경쟁자가 거의 없겠죠.
00:55:01물론 이건 매우 이론적인 이야기입니다.
00:55:07하지만 저에게는 지금 그저 재미일 뿐이에요.
00:55:08벌써 8년 전부터 읽어왔던 거라,
00:55:10마침내 어떻게 작동하는지 이해하고 싶거든요.
00:55:15마침내 어떻게 작동하는지 이해하고 싶어서요.
00:55:19리소스 디렉토리는 Inertia 앱에서 엣지 템플릿과 CSS 및 JavaScript 파일 같은 컴파일되지 않은 프론트엔드 에셋을 저장합니다.
00:55:30Resources 폴더가 있나요?
00:55:32있네요.
00:55:33Inertia 레이아웃이요.
00:55:35오.
00:55:35그건 이 React 앱이 렌더링될 원시 HTML 골격 같은 거군요.
00:55:41여기에 콘텐츠 보안 정책(CSP)을 설정할 수 있겠네요.
00:55:44일부 정적 자산을 여기로 가져올 수도 있고요.
00:55:48이미 있는 것 외에 메타데이터를 등록할 수도 있겠죠.
00:55:51알겠습니다.
00:55:52Inertia 디렉토리는 Inertia 스타터 키트를 사용하는 프로젝트에만 존재합니다.
00:55:56말이 되네요.
00:55:57프론트엔드 소스 코드를 포함하는 하위 애플리케이션을 나타냅니다.
00:56:01components, layouts, utils 같은 폴더를 추가로 만들어 구성하는 건 자유죠.
00:56:06즉, 본질적으로 거기서 View나 React 앱을 구축하는 겁니다.
00:56:09프론트엔드와 백엔드의 명확한 분리.
00:56:11Adonis.js는 백엔드와 프론트엔드 사이에 명확한 경계를 유지합니다.
00:56:14아무것도 노출하지 않기 위해, 혹은 브라우저에서 실행되지 않는 백엔드나 서버 사이드 Node.js API 때문에 충돌하지 않도록 프론트엔드 애플리케이션에 백엔드 코드를 가져오지 말아야 합니다.
00:56:30실제로는 프론트엔드가 HTTP 요청을 통해 백엔드와 통신하고 일반 JSON 데이터를 받죠.
00:56:35Adonis.js는 이를 명시적으로 모델링하도록 장려합니다.
00:56:39데이터는 API 응답을 통해 가져오고 변환됩니다.
00:56:43Inertia를 사용할 때 정확히 어떻게 통신하는지 궁금하네요.
00:56:49예를 들어 로그인을 한다고 치면,
00:56:52여기 폼이 있네요.
00:56:54네.
00:56:54흥미롭군요.
00:56:56그건 Adonis.js 프레임워크에서 제공하는 컴포넌트입니다.
00:57:01그게 경로를 받아오는데, 보통의 React 앱에서 직접 만든 REST API로 보내는 HTTP 요청을 보내는 방식과는 다르죠, 맞나요?
00:57:12대신, 우리는 이 폼이 어떤 경로에 연결되어 있는 것처럼, 마치 예전의 멀티 페이지 애플리케이션을 만들듯이 구현했습니다.
00:57:19그리고 폼을 제출하면, 아마도 이너시아(Inertia)와 아도니스(Adonis)가 내부적으로 그 요청을 생성해서 해당 경로로 전송할 겁니다.
00:57:28그리고 모든 것이 우리 대신 처리되겠죠, 본질적으로요.
00:57:31그러니 우리가 직접 요청을 보내고, 응답을 기다리고, 상태를 관리할 필요가 없습니다.
00:57:37여기서는 그렇게 할 필요가 없죠.
00:57:42네.
00:57:44새로운 프레임워크를 배울 때 당신의 사고 과정은 어떤가요?
00:57:48다른 프레임워크와 비교하시나요?
00:57:51아니면 완전히 새로운 것으로 취급하시나요?
00:57:53아마도 자동으로 이미 알고 있는 것들과 조금씩 비교하게 되는 것 같습니다.
00:57:57하지만 물론, 아도니스는 예를 들어 Next.js 등과는 너무 달라서 아주 많이 비교하지는 않습니다.
00:58:03제가 처음에 했던 주된 비교는, 이게 완전히 다른 철학을 가지고 있다는 것을 이해해야 한다는 점입니다.
00:58:10그러니까 단순히 라우팅을 하고 컴포넌트를 렌더링하는 것만이 아니라, '배터리가 포함된(batteries included)' 완전한 프레임워크를 사용하는 것이죠.
00:58:18그래서 아주 많이 비교하는 편은 아닙니다.
00:58:19꽤 새로운 것으로 보고 있죠.
00:58:21라라벨(Laravel)과는 조금 비교합니다. 왜냐하면 이게 JavaScript용 라라벨이니까요.
00:58:25그리고 아주 좋은 댓글을 남겨주신 Bleem님께 감사드립니다.
00:58:29콘텐츠를 좋아해 주셔서 기쁩니다.
00:58:32좋아요.
00:58:34공유 타입.
00:58:35프론트엔드는 여전히 아도니스가 자동으로 생성한 공유 TypeScript 타입을 활용할 수 있습니다.
00:58:40이들은 .adonis.js 클라이언트 디렉토리에 저장되며, 경로, 속성 등에 대한 타입 정의를 포함하여 우리가 TypeScript를 활용할 수 있게 합니다.
00:58:48Start.
00:58:49start 디렉토리에는 애플리케이션의 부트 라이프사이클 동안 가져오고 싶은 파일들이 포함되어 있습니다.
00:58:55예를 들어, 경로를 등록하고 이벤트 리스너를 정의하는 파일들은 이 디렉토리 안에 있어야 합니다.
00:59:00아도니스는 start 폴더에서 파일을 자동으로 불러오지 않습니다.
00:59:03아도니스는 단지 비슷한 파일들을 그룹화하는 규칙으로 사용될 뿐입니다.
00:59:06알겠습니다.
00:59:07그래서 start 폴더에 kernel TS 파일이 있는 거군요.
00:59:12아, 네, 이미 봤네요.
00:59:14맞아요, 맞아요, 맞아요.
00:59:16여기에 새로운 미들웨어를 등록할 수 있겠네요. 가이드의 일부로 해야 할지 나중에 확인해 보죠.
00:59:24테스트 폴더도 있군요.
00:59:25데이터베이스 같은 임시 파일을 위한 temp 폴더도 있고요.
00:59:28Ace.js는 ace 명령어를 실행하기 위한 진입점입니다.
00:59:33여기에 그 파일이 있나요?
00:59:35Ace.
00:59:36Ace, 네.
00:59:37알겠습니다.
00:59:38이 파일은 수정하지 마세요.
00:59:39네.
00:59:41네.
00:59:44프로젝트 매니페스트인가요?
00:59:46Lint 패키지를 사용하고요.
00:59:48Config를 사용하네요.
00:59:49네, 알겠습니다.
00:59:51개발 환경 설정.
00:59:54아도니스 애플리케이션은 완벽하게 구성된 개발 환경과 함께 제공됩니다.
00:59:57네, 코드 에디터.
01:00:01네, Edge 확장을 이미 설치했지만 이너시아와 React를 사용하면 필요 없을 것 같습니다.
01:00:09TypeScript.
01:00:10그럼 제가 이해하기로 모든 게 구성되어 있을 겁니다.
01:00:12거기에 시간 낭비하고 싶지 않아요.
01:00:14구성 및 환경.
01:00:18아도니스의 구성은 세 가지 별도 시스템으로 조직되어 있으며, 각각 특정 목적을 수행합니다.
01:00:23Config 파일은 config 폴더 안에 애플리케이션 설정을 담고 있습니다.
01:00:28환경 변수는 .env 파일에 보관되며 런타임 비밀 정보와 환경마다 바뀌는 값들을 저장합니다.
01:00:33말이 되네요.
01:00:34그건 그렇고, 비밀 정보와 관련해서, 특히 공급망 공격이 많은 요즘 제 추천은 .env 파일에 저장하지 않는 것입니다.
01:00:41강의 같은 걸 할 때는 빨리 하고 삭제하니까 .env를 사용할 수도 있겠지만요.
01:00:47하지만 계속 사용해야 하는 비밀 정보라면 InPhysical 같은 서비스를 사용하는 게 좋습니다.
01:00:53돈 받고 하는 광고는 아니고요.
01:00:54Doppler나 다른 서비스를 사용해도 됩니다.
01:00:57이것들은 클라우드 서비스입니다.
01:00:58InPhysical은 무료로 시작할 수 있고요.
01:01:01네.
01:01:02비밀 정보를 클라우드에 저장하고 CLI를 통해 불러올 수 있죠.
01:01:06만약 당신의 컴퓨터가 해킹당하더라도 비밀 정보가 그렇게 쉽게 추출되지는 않을 겁니다.
01:01:14그냥 작은 팁입니다.
01:01:17Adonis R-CTS 파일은 프레임워크 자체를 구성합니다.
01:01:20네.
01:01:20그러니까 config 파일들은 애플리케이션을 위한 것이고, 그 파일은 프레임워크 자체를 위한 것이군요.
01:01:26거기서 아마 기본적으로 찾을 폴더 이름 같은 것들을 바꿀 수 있겠네요.
01:01:33구성은 config 디렉토리에 있습니다.
01:01:35일반적인 아도니스 프로젝트에는 여러 config 파일이 포함되어 있습니다.
01:01:38봤습니다.
01:01:39이게 데이터베이스 config 파일이군요.
01:01:42메일 config고요.
01:01:43이 config 파일이 환경 변수를 어떻게 참조하는지 보세요.
01:01:46이게 환경 변수를 사용하는 올바른 방법이죠.
01:01:49네.
01:01:49말이 되네요.
01:01:50예를 들어 이메일을 보내는 애플리케이션이 있다면, 이메일 전송 방식을 설정할 수 있습니다.
01:01:55그런데 몇몇 값들은 .env 파일에 넣어서 config 파일에 하드코딩되지 않게 하는 거죠.
01:02:02그건 설정을 조정하기 위한 일반적인 구성일 뿐이고, 실제 값이나 비밀 정보는 환경 변수로 불러오는 방식입니다.
01:02:12구성 파일들은 애플리케이션 부트 사이클 중에 로드됩니다.
01:02:16Edge 템플릿은 사용하지 않고, 아마도 .env 환경 변수는 자동으로 로드되겠죠.
01:02:25앱 키는 아도니스가 쿠키 암호화, 세션 서명 및 기타 암호화 작업에 사용하는 특별한 환경 변수입니다.
01:02:35그래서 앱 키를 만들기 위해 키 생성을 실행하겠지만, 이미 하나 가지고 있습니다.
01:02:41네, 이해했습니다.
01:02:46그냥 기본값으로 충분할 거라고 생각해요.
01:02:49이번 방송을 config 파일만 조정하다가 끝내고 싶지는 않거든요.
01:02:53그럼 배포를 계속해 보죠.
01:02:58네, 알겠는데 배포하고 싶지는 않습니다.
01:03:00저는 FAQ를 만들고, 개발 쇼를 구축해서, 커뮤니티 쇼케이스 웹사이트를 만들고 싶습니다.
01:03:11이 튜토리얼에서 당신은 개발 쇼를 만들게 될 것입니다.
01:03:14네, 그렇게 하고 싶어요.
01:03:17하지만 질문 먼저 받겠습니다.
01:03:21GitHub Actions가 TanStack 보안 문제의 주범이라고 생각하시나요, 아니면 TanStack의 잘못인가요?
01:03:27기술적으로는 TanStack의 잘못입니다. 제가 기억하기로는 이런 일이 일어나지 않도록 충분히 설정을 할 수 있었거든요.
01:03:38하지만 물론, GitHub Actions처럼 이런 특정 오류가 일어나기 아주 쉽게 만드는 시스템이 있다는 것 또한 당연히 좋지는 않죠.
01:03:50글쎄요, 저는 엄청난 CI/CD 전문가는 아닙니다.
01:03:55저는 수년간 모든 종류의 자동화를 위해 GitHub Actions를 사용해 왔습니다.
01:04:00하지만 저는 여러 팀이 복잡한 워크플로우를 가지고 같은 저장소에 계속해서 푸시하는 대규모 기업에서 일하는 건 아니니까요.
01:04:12그래서 저는 GitHub Actions에 만족합니다.
01:04:14물론, 다른 형태로 사용하고 있긴 하지만요.
01:04:18전부 너무 단순한 건 아니지만, 가장 정교한 워크플로우를 사용하는 것도 아닙니다.
01:04:29JavaScript 강의에 대해 묻고 싶습니다.
01:04:3190개가 넘는 영상을 보고도 이해하지 못하는 건 잘못된 건가요?
01:04:33아무것도 이해하지 못했다면 그건 좋은 징조가 아니죠.
01:04:38아마 제 책임일 수도 있겠네요.
01:04:39하지만 그렇게 말하기는 어렵네요.
01:04:43하지만 영상을 순서대로 보셨다면 뭔가 하나는 클릭이 되어야 할 텐데요.
01:04:51하지만 제가 Windows를 쓰고 당신은 Apple을 써서 어렵게 느끼시는 것 같네요.
01:04:54그게 차이를 만들지는 않을 텐데요.
01:04:56JavaScript는 그냥 JavaScript니까요.
01:04:57글쎄요, 이해하지 못한다는 게 JavaScript 문법을 이해 못 한다는 것이라면 그건 사실 똑같습니다.
01:05:07그러니 Windows든 Apple이든 문제가 되지는 않을 겁니다.
01:05:10안녕하세요, Max.
01:05:11커뮤니티에 기여해 주셔서 감사합니다.
01:05:13안녕하세요, 정말 친절한 말씀 감사합니다.
01:05:16React와 Angular 중 무엇을 선호하시나요, 아니면 프로젝트에 따라 다른가요?
01:05:20프로젝트에 따라 다릅니다.
01:05:21하지만 요즘은 주로 React를 사용합니다. AI가 가장 좋아하는 프레임워크니까요.
01:05:26그리고 좋든 싫든 코딩할 때 AI를 정말 많이 사용합니다.
01:05:33AI 이전에는 별다른 선호도가 없었습니다.
01:05:36굳이 말하자면, Vue의 문법과 사용 편의성을 좋아했을 겁니다. 특히 Vue 2요.
01:05:42하지만 네, 그런 시절은 다 지났죠.
01:05:44뭐가 됐든 상관없습니다.
01:05:46새로운 프레임워크 패러다임 개념을 배울 때 뭐가 중요하다고 생각하시나요?
01:05:51저에게는 깊이 파고드는 게 정말 중요합니다.
01:05:54내부적으로 어떻게 작동하는지 이해하고 싶거든요.
01:05:57그냥 표면적인 대답만 얻고 싶지는 않습니다.
01:06:03어떻게 연결되어 있는지, 왜 특정 방식을 사용하는지, 언제 어떤 접근 방식을 취해야 하는지 이해하고 싶습니다.
01:06:10계속 질문을 던지고, 더 깊이 들어가 보려고 정말 노력합니다.
01:06:16그럼 이제 만들어 봅시다.
01:06:19아도니스 하이퍼미디어로 시작하고 있습니다.
01:06:22그래서 이너시아를 사용 중입니다.
01:06:23작동하게 할 수 있을지 지켜보죠.
01:06:27네, 경로들이 있습니다.
01:06:29제 여기에도 아마 있는 것 같네요.
01:06:34어디에 있죠?
01:06:35start 폴더에 있습니다.
01:06:36네, start 폴더 경로요.
01:06:39네, 여기도 있습니다.
01:06:40똑같아 보이네요.
01:06:43스타터 키트가 가입, 로그인, 로그아웃 경로를 제공하네요.
01:06:45게스트 미들웨어가 로그아웃한 사용자만 가입/로그인에 접근하게 하고, 인증 미들웨어가 로그아웃 경로를 보호하는 걸 확인하세요.
01:06:53이미 파악했습니다.
01:06:55컨트롤러 작동 방식.
01:06:56가입 컨트롤러를 보고 요청이 애플리케이션을 통해 어떻게 흐르는지 확인해 보죠.
01:07:07네, app 컨트롤러에 있는 이 새 계정 컨트롤러를 살펴봅시다.
01:07:12물론 Edge가 아니라 이너시아를 사용 중이지만, 그래도 확인해 봅시다.
01:07:19각 컨트롤러 메서드는 첫 번째 매개변수로 HTTP 컨텍스트 객체를 받습니다.
01:07:26바로 이거죠.
01:07:28컨텍스트에는 현재 요청에 대한 모든 것, 요청 데이터, 응답 객체, 인증 상태, 뷰 렌더러 등이 포함되어 있습니다.
01:07:34우리는 필요한 속성만 구조 분해합니다.
01:07:39create 메서드는 단순히 가입 폼을 보여줍니다.
01:07:42네, 이해했어요.
01:07:44우리의 경우 이너시아를 사용 중이라 이 이너시아 객체를 얻는 거죠.
01:07:48여기에는 더 많은 것들이 있습니다.
01:07:50요청을 통해 URL 같은 걸 알아낼 수도 있겠죠.
01:07:57하지만 여기서는 뷰를 렌더링하기 위해 이 이너시아 객체를 사용합니다.
01:08:01이 포스트 경로의 경우 요청 본문을 검증하기 위해 요청의 validate using 메서드를 사용하는 것 같네요.
01:08:12네.
01:08:17컨트롤러가 사용자 모델과 가입 검증기를 참조하는 걸 보셨을 겁니다.
01:08:21스타터 키트에 이미 포함되어 있죠.
01:08:23나중에 다뤄볼 겁니다.
01:08:27알겠습니다.
01:08:30컨트롤러가 뷰 렌더러를 호출하면 템플릿 파일을 찾아 렌더링합니다.
01:08:34네, 이해했습니다.
01:08:35뷰는 resources 폴더에 있습니다.
01:08:38계정을 만들어 보세요.
01:08:39이미 했습니다.
01:08:40명령줄과 REPL.
01:08:42기능을 바로 만들기 시작하지 않고 왜 CLI와 REPL을 다루는지 궁금하실 겁니다.
01:08:46이유는 이렇습니다.
01:08:46이 튜토리얼을 진행하면서 컨트롤러, 모델 및 기타 파일을 만들기 위해 계속 ACE 명령어를 사용하게 될 겁니다.
01:08:51지금 CLI에 익숙해져야 나중에 흐름이 끊기지 않습니다.
01:08:54좋습니다.
01:08:55node ace list 명령어로 사용 가능한 명령들을 볼 수 있습니다.
01:08:59해보죠.
01:09:02안녕하세요.
01:09:05알겠습니다.
01:09:06네, 실행할 수 있는 내장 명령어들이 많이 있네요.
01:09:11하나 이상의 아도니스 패키지를 설치하고 구성하기.
01:09:14빌드, 꺼내기, 시더를 실행하기 위한 데이터베이스 명령어들.
01:09:19키 생성.
01:09:21경로 나열하기.
01:09:23리스너를 설정할 이벤트를 만드는 것 같은 생성 명령어들.
01:09:29아니면 새로운 미들웨어나 모델 만들기.
01:09:32좋습니다.
01:09:32네.
01:09:38REPL.
01:09:39네.
01:09:40먼저 앞으로 좀 나가겠습니다.
01:09:42모험 좀 해보죠.
01:09:54이 챕터에서는 포스트와 댓글 리소스를 위한 모델과 마이그레이션을 만들 겁니다.
01:09:59관계 설정하기.
01:10:01더미 데이터를 생성합니다.
01:10:01네.
01:10:02이 장에서는 AdonisJS의 SQL ORM인 Lucid를 소개합니다.
01:10:06원시 SQL 쿼리를 작성하는 대신, 자바스크립트 클래스인 모델을 사용하여 데이터베이스 테이블을 표현하게 됩니다.
01:10:11이미 이전에 말씀드린 내용이죠.
01:10:14중요한 차이점이 있습니다.
01:10:15모델은 데이터와 상호작용하는 방식을 정의하지만, 데이터베이스 구조를 수정하지는 않습니다.
01:10:20그건 마이그레이션이 할 일이죠.
01:10:21네.
01:10:21모델이 있고,
01:10:22코드에서 그 모델을 사용합니다.
01:10:24데이터를 다루기 위해서요.
01:10:25하지만 그러려면 데이터베이스 테이블이 필요합니다.
01:10:27그리고 테이블을 만들기 위해서는 마이그레이션이 필요합니다.
01:10:31일종의 작은 스크립트라고 할 수 있죠.
01:10:33우리가 필요한 올바른 테이블을 갖추도록 데이터베이스를 조정하는 청사진 같은 겁니다.
01:10:37그리고 마이그레이션은 모델에서 파생됩니다.
01:10:40모델 클래스에서요.
01:10:42라라벨에서는 그렇게 작동합니다.
01:10:43아도니스(Adonis)에서도 마찬가지로 작동한다는 것을 이해했습니다.
01:10:49자, 그럼 포스트 모델을 추가해 봅시다.
01:10:54node ace make:model post 명령어로요.
01:11:01그럼 모델즈 포스트 TS 파일이 생성됩니다.
01:11:05그리고 새로운 마이그레이션 파일도 이미 만들어졌죠.
01:11:09말했듯이 모델과 마이그레이션 둘 다 필요하니까요.
01:11:11이제 여기에 포스트 모델이 생겼습니다.
01:11:13지금은 빈 클래스 상태네요.
01:11:19그건 아마 제 IDE의 타입스크립트 오류일 수도 있어요.
01:11:26아니면, 아뇨.
01:11:27아직 하나도 없다고 생각해요.
01:11:28왜냐면 아직 마이그레이션을 설정하지 않았으니까요.
01:11:33스키마 말이에요.
01:11:34테이블 형태를 아직 정의 안 했어요.
01:11:41마이그레이션에서 테이블 구조를 정의해야 합니다.
01:11:43맞아요.
01:11:43그래서 그 작업을 해야 합니다.
01:11:45자, 이걸 복사할게요.
01:11:48그리고 이 포스트 파일로 갑시다.
01:11:53마이그레이션 파일이죠.
01:11:54베이스 스키마.
01:11:56네.
01:11:56이게 맞네요.
01:11:59처음에는 posts라는 테이블 이름으로 기본적인 테이블을 설정합니다.
01:12:07그리고 ID와 타임스탬프를 부여하죠.
01:12:10우리가 지금 추가하려는 건 이 세 줄입니다.
01:12:14이걸 복사할 수 있어요.
01:12:16여기에 붙여넣죠.
01:12:18모든 포스트에 제목, URL, 요약을 추가합니다.
01:12:24그런 것 같네요.
01:12:25그리고 값이 들어가야 하니 not nullable로 설정합니다.
01:12:28null이면 안 됩니다.
01:12:30좋습니다.
01:12:31이제 이걸 실행해야 할 것 같네요.
01:12:34네.
01:12:34업(up) 부분은 생성하는 역할을 합니다.
01:12:36다운(down) 부분은 테이블을 삭제하죠.
01:12:39댓글 모델 생성.
01:12:40댓글 모델을 만들어 봅시다.
01:12:43자, 그럼.
01:12:45개발 서버를 중지할게요.
01:12:47변경 사항을 적용하려면 재시작해야 할 테니까요.
01:12:51다시 모델을 생성하겠습니다.
01:12:53댓글 모델이요.
01:12:54전과 똑같이요.
01:12:56새 마이그레이션 파일이 생겼네요.
01:12:57새로운...
01:13:01어디 있지?
01:13:02새 모델.
01:13:03여기 댓글 모델이 있네요.
01:13:04그리고 마이그레이션 파일에요.
01:13:06다시 기본 뼈대입니다.
01:13:09여기에 뭘 추가하죠?
01:13:10내용을 위한 한 줄인 것 같네요.
01:13:13내용요.
01:13:18좋습니다.
01:13:21이제 테이블을 추가하기 위해 실행할 수 있습니다.
01:13:27잘 작동하는군요.
01:13:29한번 봅시다.
01:13:31개발 서버를 다시 시작하니 잘 되네요.
01:13:35이제 그 에러도 사라졌습니다.
01:13:36마이그레이션을 적용하니 뒤에서 타입을 생성했나 봅니다.
01:13:47네.
01:13:48데이터베이스 스키마 TS 파일이 업데이트되었습니다.
01:13:51여기 데이터베이스 폴더 안의 스키마 TS 파일이요.
01:13:56이 파일은 기본적으로 관리되는 거죠.
01:13:58아도니스가 생성한 스키마가 들어 있습니다.
01:14:02결국 에이스 명령어가 만든 거죠.
01:14:04답변 감사합니다, Max.
01:14:11당신 강의를 여러 개 샀어요.
01:14:12소프트웨어 개발자로 성장하는 데 큰 도움이 됐습니다.
01:14:16네.
01:14:17정말 감사합니다.
01:14:18강의를 들어주셔서 감사하고 좋은 말씀도 감사합니다.
01:14:21도움이 되었다니 정말 기쁩니다.
01:14:24당신의 커리어에 작은 부분이라도 기여할 수 있어 영광입니다.
01:14:27정말 감사합니다.
01:14:31자, 이제 관계를 설정해 봅시다.
01:14:33댓글과 포스트 사이의 관계를 만들고 싶으니까요.
01:14:37모든 포스트는 하나 이상의 댓글을 가질 수 있죠.
01:14:44그리고 모든 댓글은 딱 하나의 포스트에 속합니다.
01:14:47즉, 일대다 관계입니다.
01:14:54댓글은 포스트에 속하고, 포스트는 사용자에게 속하죠.
01:14:57맞아요.
01:14:57그것도 필요합니다.
01:14:58외래 키를 위한 마이그레이션을 생성합시다.
01:15:01다음 명령어로 기존 테이블을 수정하는 새로운 마이그레이션 파일을 만듭니다.
01:15:04한번 보죠.
01:15:06무슨 동작을 하죠?
01:15:09외래 키 추가 마이그레이션을 생성합니다.
01:15:11그냥 추가적인 마이그레이션 파일이네요.
01:15:13전에는 모델과 마이그레이션 파일이 같이 나왔는데.
01:15:16이제는 마이그레이션 파일만 사용하네요.
01:15:22모델이나 클래스가 필요 없으니까요.
01:15:25하지만 데이터베이스 설정을 조정하고 몇 개의 테이블에 새로운 컬럼을 추가하기 위해 마이그레이션이 필요합니다.
01:15:40관계 설정을 위해 컬럼을 추가하려는 건데, SQL 데이터베이스에서의 관계는 단순히 컬럼을 추가해 이 댓글은 이 사용자 ID와 포스트 ID를 가진다고 말하는 것입니다.
01:15:53복사할게요.
01:15:55복사하겠습니다.
01:15:56이게 포스트에 대한 외래 키들입니다.
01:16:00내가 만든 게 맞나?
01:16:05외래 키 추가.
01:16:11잠시만요.
01:16:22아, 이제 실행할 거예요.
01:16:23실행 안 했었나?
01:16:25아니요, 이거예요.
01:16:28파일 이름이 좀 다르네요.
01:16:31뭐, 상관없어요.
01:16:34붙여넣죠.
01:16:35무슨 작업을 하죠?
01:16:37포스트 테이블에 양수 정수 타입의 not null인 사용자 ID 컬럼을 추가합니다.
01:16:45null이 아니고요.
01:16:45그리고 외래 키 관계를 설정합니다.
01:16:48사용자가 삭제되면 포스트도 삭제되도록요.
01:16:51댓글의 경우 사용자뿐 아니라 포스트와도 연결하는데, 댓글은 포스트에 속하기 때문입니다.
01:16:58그리고 변경 사항을 되돌리는 다운 마이그레이션이 있습니다.
01:17:01좋아요.
01:17:03실행해 봅시다.
01:17:07실행합니다.
01:17:11개발 서버가 시작됩니다.
01:17:13포스트 모델에서 관계를 정의합시다.
01:17:15데이터베이스에 외래 키 컬럼이 생겼으니 모델을 업데이트해 관계를 정의할 겁니다.
01:17:20복사할게요.
01:17:22지금은 비어 있는 포스트 모델로 가서 붙여넣습니다.
01:17:27이제 그 클래스 안에서 댓글 프로퍼티를 추가해 관계를 선언합니다.
01:17:43아도니스에서 제공하는 데코레이터를 사용해 포스트가 많은 댓글을 가지고 있고 사용자에 속한다고 명시합니다.
01:17:57라라벨과 거의 같은 방식이네요.
01:18:02읽고 이해하기 꽤 쉽네요.
01:18:05단순히 프로퍼티를 선언만 한다는 게 좀 이상할 수도 있겠네요.
01:18:11초기값이 없으면 타입스크립트에서 문제가 생기기에 declare가 필요합니다.
01:18:18declare를 쓰면 문제가 안 생겨요.
01:18:20그게 답니다.
01:18:22댓글 모델에도 관계가 필요합니다.
01:18:26거기에도 붙여넣죠.
01:18:28댓글은 포스트에 속하고 사용자에게 속한다고 정의합니다.
01:18:31어디서 보느냐에 따라 has와 belong 관계가 결정되죠.
01:18:36둘 다 설정하는 겁니다.
01:18:39알겠습니다.
01:18:40이제 됐습니다.
01:18:41이 튜토리얼에서는 필요 없으니 사용자 모델에는 역방향 has many를 추가하지 않았습니다.
01:18:46나중에 애플리케이션에서 사용자의 포스트나 댓글을 조회할 필요가 생기면 추가할 수 있습니다.
01:18:51네.
01:18:52이 튜토리얼에서는 사용자가 쓴 모든 포스트를 찾을 일은 없을 것 같네요.
01:18:58그래서 관계를 설정할 필요는 없지만, 사용자의 모든 포스트를 조회해야 한다면 당연히 설정해야 하겠죠.
01:19:04하지만 여기서는 모든 포스트를 조회하고 포스트가 어떤 사용자의 것인지 표시만 하면 됩니다.
01:19:16세상에, 이 프레임워크는 앵귤러와 네스트가 아이를 낳은 것 같네요.
01:19:20크고 복잡한 프레임워크도 여전히 관련이 있나 보네요.
01:19:24이 프레임워크는 이론상 꽤 훌륭할 수 있어요. 많은 것이 내장되어 있으니까요.
01:19:30처음에는 압도적일 수 있지만요.
01:19:33큰 장점은 따로 뭘 추가할 필요가 없다는 겁니다.
01:19:39전혀 안 넣는 건 아니지만, 많은 경우에 추가할 게 거의 없이 다양한 애플리케이션에 작동하게 만들 수 있죠.
01:19:48저는 Next.js, Tanstack, Start의 큰 팬이지만 아도니스는 써본 적이 없거든요.
01:19:54그러니 최고니까 써보세요라고 말할 수는 없죠.
01:19:56저 스스로 써보지는 않았으니까요.
01:19:58하지만 Next.js나 Tanstack, 앵귤러 같은 걸 쓸 때는 인증을 위해 항상 라이브러리를 추가해야 하죠.
01:20:07데이터베이스를 위해서도 추가적인 라이브러리나 ORM, 프리즈마 같은 걸 더해야 하고요.
01:20:14공급망 공격 위협이 있는 요즘 같은 때에, 그런 라이브러리를 다 꿰어 맞추는 건 꽤 골치 아픈 일일 수 있습니다.
01:20:21유지보수 문제도 있고요.
01:20:23이론적으로는 훌륭할 것 같네요.
01:20:26AI 없이 작업하시니 기분 좋으면서도 향수가 느껴지시나요?
01:20:30아, 네.
01:20:31향수를 불러일으키는 느낌이네요.
01:20:33작동 방식을 이해하고 싶어서 AI를 안 쓰고 있습니다.
01:20:38요즘 같은 시대에도 무언가 배울 땐 그게 중요하다고 생각해요.
01:20:42그냥 AI로만 코딩하면 재미있는 라이브 방송도 안 될 거고요.
01:20:47그리고 Vibe coding으로는 한계가 분명히 있습니다.
01:20:52모델과 테이블이 준비되었으니 개발과 테스트를 위해 더미 데이터를 채워야 합니다.
01:20:57팩토리는 실제 같은 가짜 데이터로 모델 인스턴스를 만드는 청사진 역할을 합니다.
01:21:03청사진을 한 번만 정의하면 되죠.
01:21:04좋습니다.
01:21:05포스트 팩토리를 만들어 가짜 포스트를 생성해 봅시다.
01:21:10팩토리 만들기 명령어를 실행합니다.
01:21:14포스트 팩토리 파일이 생기는데, 여기서 포스트 생성 방식을 정의합니다.
01:21:21복사해서 살펴볼게요.
01:21:25가짜 포스트를 만드는 겁니다.
01:21:30Faker 도구가 포스트를 생성할 때 선택할 수 있는 제목 리스트가 있습니다.
01:21:37가짜 URL과 Lorem Ipsum 단락도 추가하고요.
01:21:45프로그래밍 방식으로 빠르게 가짜 데이터를 만드는 거죠.
01:21:48요즘은 AI로도 할 수 있겠죠.
01:21:51하지만 토큰 비용도 안 드는 옛날 방식을 쓰겠습니다.
01:21:56댓글 팩토리도 만들어 봅시다.
01:22:02여기 있습니다.
01:22:03그리고 그 코드를 여기에 복사해 넣죠.
01:22:06여기서는 그냥 더미용 로렘 입숨 문단 몇 개를 출력합니다.
01:22:11이제 시더가 필요합니다.
01:22:12자, 이제 가짜 데이터를 만들어낼 팩토리가 생겼습니다.
01:22:15그런데 이 가짜 데이터로 뭘 하려고 할까요?
01:22:17데이터베이스에 넣어야겠죠.
01:22:19그럴 때 시더가 필요한 겁니다.
01:22:23그래서 하나 만들어 보려고요.
01:22:24이건 일반적인 개념이죠.
01:22:26팩토리 시더요.
01:22:27아도니스에서만 쓰는 게 아닙니다.
01:22:28라라벨에서도 볼 수 있죠.
01:22:29아니면 어떤 환경에서든 이런 개념을 활용할 수 있습니다.
01:22:37또한, XJS 애플리케이션에서도 데이터를 채워 넣고 싶은 데이터베이스가 있을 수 있고요.
01:22:41물론 'node ace' 명령어는 아도니스 전용이긴 합니다.
01:22:45이제 시더를 만들어 보겠습니다.
01:22:47포스트 시더를 한번 살펴보죠.
01:22:49이 파일은 초기 데이터를 데이터베이스에 밀어 넣을 때 실행할 수 있습니다.
01:22:54그러니 여기서 무엇을 할지 정의해야 합니다.
01:22:57제 생각엔 여기서 더미 게시물과 댓글을 만들고, 연결한 다음, 데이터베이스에 넣는 작업을 수행해야 할 것 같습니다.
01:23:07아도니스에선 어떻게 하는지 볼까요.
01:23:10붙여넣고 한번 살펴보죠.
01:23:13찾거나 아니면 실패하게 만들고 있네요.
01:23:17사용자를 찾으려는 중입니다.
01:23:19그리고 실패해야겠죠.
01:23:21만든다는 말은 없고, 아직 그 사용자가 없으니까요.
01:23:25그래서 일단 사용자를 찾으려고 합니다.
01:23:26만들어야겠죠.
01:23:28그다음 이 사용자를 병합하고요.
01:23:33새 게시물을 여러 개 만들어서 이 명령어로 해당 사용자에게 연결합니다.
01:23:37그리고 각 게시물마다 더미 댓글을 생성하고, 그 댓글들 또한 게시물과 사용자에게 연결합니다.
01:23:46댓글 개수는 랜덤으로 설정하고 3을 더하고요.
01:23:51즉, 게시물 하나당 최소 3개에서 최대 6개의 댓글이 생성되는 거죠.
01:24:02댓글이 3개에서 6개 사이입니다.
01:24:06우선, 이 이메일로 사용자를 가져옵니다.
01:24:09이건 지난 챕터에서 O로 만들었던 바로 그 사용자입니다.
01:24:12제가 직접 사용자를 만들게요.
01:24:15빨리 처리하겠습니다.
01:24:19서버를 실행하죠.
01:24:23아직 시더를 실행해 보진 않았거든요.
01:24:26그건 잘 될 겁니다.
01:24:27그런데 그건 틀렸어요.
01:24:28빨리 회원가입을 할게요.
01:24:34네, 네, 네.
01:24:36아니, 이건 아니고요.
01:24:37자.
01:24:39저는 이걸 사용할 겁니다.
01:24:44좋아요.
01:24:45다른 이메일 주소를 사용하겠습니다.
01:24:47당연히 저는...
01:24:50test@example.com을 쓸게요.
01:24:52네.
01:24:53자, 됐습니다.
01:25:02네, 계속 진행해 주세요.
01:25:03이런 스트림이라.
01:25:03마음에 듭니다.
01:25:04네, 당연히 계속할 겁니다.
01:25:06참고로, 매주 목요일마다 이런 스트림을 진행할 계획입니다.
01:25:13다음 주에는 아마 못 할 것 같아요.
01:25:16다른 약속들이 잡혀 있거든요.
01:25:18하지만 기본적으로는 매주 목요일, 오후 5시(중앙 유럽 여름 시간 기준)에 시작할 계획입니다.
01:25:26제 생각에 Anthropic은 정말로 자사 도구들을 밀어주려는 것 같아요.
01:25:40Claude Code 외에는 아무것도 안 밀어주려고 하죠.
01:25:42사용자들을 묶어두려는 겁니다.
01:25:43어떻게 될지 지켜보죠.
01:25:45Claude를 다른 모든 도구에서 사용할 수 있게 될지는 잘 모르겠어요.
01:25:51적어도 토큰 비용을 지불하는 구독 모델로는 아니겠죠.
01:25:55물론 API를 쓰면 가능하겠지만.
01:25:58그건 물론 엄청나게 비쌀 거고요.
01:26:02자, 이제 시더를 실행해 보겠습니다.
01:26:06서버를 중지하고요.
01:26:09실행합니다.
01:26:10그럼 이제 더미 데이터가 만들어졌습니다.
01:26:12앱이 완전히 준비되지 않았어도 이미 확인할 수 있을 겁니다.
01:26:17데이터베이스를 보면 posts와 comments 테이블이 보일 거예요.
01:26:21users 테이블에는 제가 만든 사용자가 있네요.
01:26:23posts 테이블에는 더미 게시물들이 들어 있습니다.
01:26:27comments 테이블에도 더미 댓글들이 있네요.
01:26:33각 댓글은 특정 사용자 및 게시물 ID와 연결되어 있습니다.
01:26:36사용자는 하나뿐이니까요.
01:26:38그래서 모두 한 명의 사용자에 연결된 겁니다.
01:26:40시더에 그렇게 썼으니까요.
01:26:41게시물을 보면, 여기 댓글이 5개 달린 게시물이 있네요.
01:26:47여기에는 댓글이 3개 달려 있고요.
01:26:50우리가 작성한 대로 잘 됐습니다.
01:26:52네.
01:26:53이제 여기서 뭘 했는지 이해가 갑니다.
01:26:55REPL로 데이터를 쿼리할 필요는 없겠네요.
01:26:57이미 데이터가 있는 걸 봤으니까요.
01:27:00하지만,
01:27:01코드를 좀 살펴보죠.
01:27:02재미있는 점은, 이런 모델 기반 접근 방식에서는 SQL 쿼리를 직접 작성할 필요가 없다는 겁니다.
01:27:10대신 모델을 사용하거나 전체 모델에 접근하죠.
01:27:17모델에 그 이름을 등록했기 때문에 이 모델이 존재하는 겁니다.
01:27:20그리고 'all' 같은 메서드를 써서 게시물을 전부 가져올 수 있습니다.
01:27:24아니면 여기 보는 것처럼 where 절을 사용해 게시물 개수를 제한하는 쿼리를 작성할 수도 있고요.
01:27:30이게 바로 ORM이 작동하는 방식입니다.
01:27:32SQL 문을 직접 쓰지 않고도 프로그래밍 방식으로 데이터를 쿼리하는 거죠.
01:27:37사실 전 ORM의 열렬한 팬은 아닙니다. 싫어하는 건 아니지만 평소에 잘 안 써요.
01:27:46특히 요즘은 AI 덕분에 원시 SQL 쿼리 작성이 그 어느 때보다 쉬워졌으니까요.
01:27:52그래서 저는 보통 애플리케이션에서 원시 SQL 쿼리를 사용합니다.
01:27:58ORM을 안 쓰는 거죠.
01:28:00원시 쿼리를 작성하면 모든 성능을 온전히 활용할 수 있으니까요.
01:28:06사실 AI가 나오기 전에도 쿼리가 그렇게 어렵지는 않았습니다.
01:28:10조인이 포함되어도 별로 어렵지 않거든요.
01:28:13물론 SQL 쿼리가 꽤 복잡해질 때도 있고 AI가 틀릴 때도 있습니다.
01:28:18하지만 제 경험상 꽤 복잡한 쿼리도 꽤 잘 작성합니다.
01:28:22그건 그냥 제 의견일 뿐이고요.
01:28:24어쨌든 여기선 ORM을 쓰니 저도 그대로 따르겠습니다.
01:28:29라우트, 컨트롤러, 그리고 뷰.
01:28:32지난 챕터에서 저희는 데이터베이스 테이블과 관계를 갖춘 포스트 및 댓글 모델을 만들었죠.
01:28:37이제 사용자가 게시물을 볼 수 있는 페이지를 구축해 그 모델들에 생명력을 불어넣을 겁니다.
01:28:46게시물과 댓글은 데이터베이스에만 존재하니까요.
01:28:49서버는 이미 켜져 있습니다.
01:28:52자, 이제 새로운 컨트롤러를 만들어 봅시다.
01:28:54제가 이해하기로 이 프레임워크의 개념은 라우트, 컨트롤러, 뷰를 갖추는 것입니다.
01:29:02그러니 아주 예전부터 있었던 MVC 방식이죠.
01:29:07모델 뷰 컨트롤러요.
01:29:09웹 애플리케이션뿐만 아니라 다양한 애플리케이션을 구조화하는 아주 오래된 방식입니다.
01:29:18오래됐다고 나쁘다는 건 아닙니다. 확실히 말씀드리고 싶네요.
01:29:24하지만 요즘 Next.js 같은 애플리케이션들에서는 잘 안 보이죠.
01:29:30그래서요.
01:29:31다른 탭에서 새로운 포스트 컨트롤러를 만들어 실행하겠습니다.
01:29:38빈 파일이 생겼고, 여기서 4개의 서로 다른 라우트에서 수행할 작업을 정의하는 메서드들을 등록할 겁니다.
01:29:44시간을 절약하기 위해 이 코드도 복사해 넣겠습니다.
01:29:47여기에 인덱스 메서드가 있습니다.
01:29:52HTTP 컨텍스트를 가져오는데, 이건 다양한 데이터를 제공해 주는 녀석이죠.
01:29:57사실 이걸 수정해야 합니다. 왜냐하면 저는 Inertia를 사용해야 하거든요.
01:30:05Inertia를 써야 하니 당연히 그 컴포넌트도 추가해야 합니다.
01:30:09우리가 여기서 하는 건 모델 파일에서 정의했던 포스트 클래스를 사용하는 겁니다.
01:30:16앞서 정의했던 바로 그 포스트 클래스 말이죠.
01:30:20ORM이 작동하는 방식인데, 쿼리를 생성 중입니다.
01:30:24사용자 정보를 미리 로딩(preloading)하면, 게시물을 가져올 때마다 다시 가져오지 않아도 될 겁니다.
01:30:32그리고 게시물을 생성 날짜 내림차순으로 정렬하고요.
01:30:38그러면 가장 최신 게시물이 첫 번째로 오게 되겠죠.
01:30:45그리고 렌더링을 할 건데, 추가가 필요할 겁니다.
01:30:53라우트를 정의하죠.
01:30:56자, 이제 'start routes' 파일로 이동합니다.
01:31:03여기에 추가했어요.
01:31:07물론 어디에나 추가할 수 있죠.
01:31:10그런데 왜 안 되는 거죠?
01:31:12왜 작동을 안 하지?
01:31:13개발 서버를 다시 시작해야 하나요?
01:31:16아, 잘못했네요.
01:31:18아, 개발 서버를 안 켰었군요.
01:31:21제 실수입니다.
01:31:22자, 이제 다시 켜겠습니다. 그러면 되겠죠.
01:31:25이제 자동으로 생성되었습니다.
01:31:26아도니스가 즉석에서 여러 타입 정의를 만들고 있는 겁니다.
01:31:31이제 됐습니다.
01:31:34아도니스에게 '/posts'로 들어오는 GET 요청을 처리하라고 알려줍니다.
01:31:40PostsController의 인덱스 메서드를 실행하라고요.
01:31:43그게 바로 이 메서드입니다.
01:31:47이제 뷰가 필요합니다.
01:31:50새 뷰를 만들어 보죠.
01:31:53이 명령어가 Inertia 뷰를 만드는 게 맞는지는 모르겠네요.
01:31:56어디 봅시다.
01:31:59'node ace list'를 입력해서 무엇을 만들 수 있는지 확인할 수 있습니다.
01:32:04뷰를 만들 순 있는데 그건 Edge.js 템플릿 파일이거든요.
01:32:08그건 아니에요.
01:32:11직접 만들어야 하나 봅니다.
01:32:20직접 만들어야겠네요. 그리 어렵진 않을 겁니다.
01:32:24자, Inertia 페이지로 이동하죠.
01:32:29여기에 'posts'를 추가할 겁니다.
01:32:31그 안에 'index.js' 파일을 만들면 되겠죠?
01:32:34그리고 기본 함수를 export할 거고요.
01:32:38이름은 'Posts'라고 짓겠습니다.
01:32:41그리고 '내 게시물' 같은 내용을 넣죠.
01:32:44그냥 평범한 React 컴포넌트입니다.
01:32:50자, 에러가 없어졌네요.
01:32:51방금 그 파일을 추가했으니까요, 맞죠?
01:32:55'posts' 폴더와 'index.js' 파일을 추가했습니다.
01:32:58제가 말한 게 바로 그거예요.
01:32:59이렇게 연결되는 겁니다.
01:33:02이제 개발 서버가 돌아가고 있으니까,
01:33:05여기로 가서 '/posts'를 치면 게시물들이 보일 겁니다.
01:33:09잘 작동하네요.
01:33:10이제 컨트롤러에서 데이터를 가져와 컴포넌트로 전달해야 합니다.
01:33:20사실 props로 벌써 전달하고 있다고 생각합니다.
01:33:23문제는 여기서 이걸 받을 수 있느냐는 거죠.
01:33:30어떻게 해야...
01:33:33아, children을 써야 하나 봅니다.
01:33:36다른 파일에선 어떻게 하고 있죠?
01:33:40데이터를 전달하는 파일이 있나요?
01:33:45없네요.
01:33:46없어요, 없어요.
01:33:50레이아웃에 있죠?
01:33:52children.
01:33:54이게 제가 필요한 거라고 생각합니다.
01:33:58이렇게 해본 적이 없어서,
01:34:00알아봐야겠네요.
01:34:19여기에 있는 게 아닌가요?
01:34:23아, 맞다. 제 잘못입니다.
01:34:25이건 올바른 TypeScript 코드가 아니죠.
01:34:27이렇게 해야 합니다.
01:34:30좋아요.
01:34:30이제 import들을 추가해야 합니다.
01:34:36import 추가요.
01:34:38이게 맞다고 봅니다.
01:34:40이게 우리가 가진 건가요?
01:34:41데이터, 생성된 데이터.
01:34:42네.
01:34:43좋아요.
01:34:45좋아요.
01:34:45자, 이제 한번 봅시다.
01:34:46여기에 프래그먼트를 추가할 수 있을까요?
01:34:50그리고 이런 식으로 하는 거죠.
01:34:54Children이요.
01:34:57Props.
01:34:59음.
01:35:02사용자요.
01:35:03아니, 공유 프롭이 아닐 수도 있어요.
01:35:04어떻게 가져와야 하죠?
01:35:06아마도 제가 파고들어야 할 것 같아요.
01:35:10어디 봅시다.
01:35:11아마도 Inertia 문서를 좀 살펴봐야겠네요.
01:35:15가이드.
01:35:17Inertia가 어디 있지?
01:35:19컴포넌트에 프롭을 어떻게 전달하더라?
01:35:24필요한데.
01:35:25트랜스포머가 필요한가?
01:35:29모델 인스턴스를 일반 객체로 직렬화하려면 트랜스포머를 사용하세요.
01:35:33알겠어요.
01:35:34알겠어요.
01:35:35알겠어요.
01:35:37좋아요.
01:35:37그래서 여기서는 조금 편법을 쓸 겁니다.
01:35:40AI를 좀 가져오죠.
01:35:44음.
01:35:48Adonis JS 앱을 빌드 중인데.
01:35:51JS 앱.
01:35:53게시물을 가져와야 해요.
01:35:56보세요.
01:35:57포스트 컨트롤러.
01:36:02제 포스트 Inertia로요.
01:36:04이런.
01:36:05이게 당신에게 읽기 좋게 안 보인다는 걸 알아요.
01:36:10좋아요.
01:36:10좋아요.
01:36:10잠시만요.
01:36:18좋아요.
01:36:18Adonis JS 앱을 빌드 중인데.
01:36:20JS 앱.
01:36:21게시물을 가져와야 해요.
01:36:24제 포스트 컨트롤러에서요.
01:36:27추가.
01:36:28이런.
01:36:28포스트 컨트롤러 추가.
01:36:30컨트롤러.
01:36:32제 게시물로.
01:36:35뷰.
01:36:35Inertia.
01:36:37뷰.
01:36:39그러니까, 그건 여기 posts 폴더의 index예요.
01:36:45이 페이지들로 연결할 겁니다.
01:36:48확인 부탁드려요.
01:36:50그리고.
01:36:56그걸 하도록 도와줘.
01:36:59그걸 알아낼 수 있는지 봅시다.
01:37:03음.
01:37:03ORM은 스키마 변환과 협업에 좋죠.
01:37:07음.
01:37:08네.
01:37:09또 말하지만.
01:37:09전 말 안 해요.
01:37:10ORM은 나쁘다.
01:37:11단지.
01:37:11제 프로젝트에는 딱히 필요가 없을 뿐이죠.
01:37:14하지만 또.
01:37:14대규모 팀에서 일하거나 하는 건 아니라서요.
01:37:18음.
01:37:18전.
01:37:19분명히.
01:37:20말하죠.
01:37:21마이그레이션은 사용한다고 말이죠.
01:37:23그러니까.
01:37:23딱히.
01:37:24안 하는 건.
01:37:25마이그레이션을 안 쓰는 게 아니에요.
01:37:26그런 거랑은 다르죠.
01:37:27음.
01:37:28그냥 저도.
01:37:29직접 작성합니다.
01:37:30아니면.
01:37:31LLM이 작성하게 하죠.
01:37:32그래서 마이그레이션은 정말 유용해요.
01:37:34전 그냥 ORM 부분이 필요 없는 것뿐이에요.
01:37:36궁금했거든요.
01:37:36와이프 코딩에 대해 물어보는 게.
01:37:37좋은지 아닌지.
01:37:38전 생각 안 해요.
01:37:39와이프 코딩이.
01:37:40와이프 코딩이.
01:37:41좋다고 생각하지 않아요.
01:37:42심각한 작업을 하기에는요.
01:37:43하지만.
01:37:44와이프 코딩은.
01:37:44분명히 아주 좋죠.
01:37:45그저 필요한 거라면.
01:37:46빠른.
01:37:47유틸리티 도구.
01:37:48아니면 그저.
01:37:48일 처리를 빠르게 해야 한다면요.
01:37:50맞아요.
01:37:50소프트웨어를 만들고 있다면.
01:37:51판매할 거라면.
01:37:52만약에.
01:37:53심각한 작업을 하고 있다면.
01:37:55배포할 계획이 있거나.
01:37:56아니면 직장에서라면.
01:37:57와이프 코딩은.
01:37:58그냥 놓치게 되죠.
01:37:59모든 흐름을요.
01:38:00이해를 못 하는 거예요.
01:38:01코드베이스에서 무슨 일이 일어나는지를요.
01:38:02결국엔.
01:38:03벽에 부딪힐 거예요.
01:38:04그리고.
01:38:04어떻게 계속할지 모를 거고요.
01:38:06그러니까.
01:38:07코드에 신경 쓰지 않는 건.
01:38:08좋은 해결책이 아닙니다.
01:38:09그런.
01:38:09그런 상황에서는요.
01:38:11하지만 또.
01:38:11그저 필요한 거라면.
01:38:13음.
01:38:13빠른.
01:38:15도구라면.
01:38:16내 컴퓨터에서 돌릴.
01:38:17아니면 그런 비슷한 거라면요.
01:38:18신경 안 써도 될지도 모르죠.
01:38:19음.
01:38:20세세한 것까지.
01:38:21모든 디테일.
01:38:23그런 것들요.
01:38:24그러니까 네.
01:38:25와이프 코딩이 거기선 좋을 수 있죠.
01:38:26그리고 네.
01:38:26라이브 코딩은 언제나.
01:38:28언제나 도전적이죠.
01:38:29하지만 AI가 해결할 수 있을지 봅시다.
01:38:31여기서 린팅 오류와 씨름하고 있는 것 같네요.
01:38:36하지만 네.
01:38:38전 그냥 AI를 쓰고 있어요.
01:38:40그나저나.
01:38:40평소라면 제가 직접 들어가서.
01:38:41제 손으로 빌드했을 거예요.
01:38:44하지만 20분 안에 나가야 해서요.
01:38:47차라리 완성된 코드를 지금 받고.
01:38:49그걸 검토하며 작업하는 게 나아요.
01:38:51그리고 지금.
01:38:52그 후에 여기에서 계속할 수 있게 말이죠.
01:38:54그게 주된 이유예요.
01:38:56그래서 네.
01:38:56지금 뭔가 했네요.
01:38:58먼저 이게 작동하는지 봅시다.
01:39:00제가 설명하기 전에요.
01:39:02그리고 얘가 뭘 했는지 보죠.
01:39:04그런데 좋아 보이네요.
01:39:05스타일링은 완전히 엉망이지만.
01:39:07보다시피.
01:39:10여기 게시물들이 렌더링되고 있어요.
01:39:12제 사용자와 연결된 것도 볼 수 있고요.
01:39:16그러니까.
01:39:17컨트롤러에서 데이터를 보내는 게.
01:39:19작동하는 것 같네요.
01:39:19자 이제 봅시다.
01:39:20어떻게 작동하는지.
01:39:22그리고 또.
01:39:22공식 문서 링크를 줬었어요.
01:39:24그러니까 그건 그냥.
01:39:28그냥.
01:39:31문서가 가르쳐 줬을 법한 내용이죠.
01:39:34그러니까 포스트 컨트롤러에서.
01:39:36게시물을 가져오고.
01:39:38여기서 바뀐 유일한 것은.
01:39:39본질적으로.
01:39:40우리가.
01:39:41게시물을 전달한다는 거죠.
01:39:42하지만 이렇게 날것 그대로가 아니라.
01:39:43대신.
01:39:44포스트 트랜스포머의 도움을 받아.
01:39:45그게 새로 추가된 거예요.
01:39:47그러니까 이 파일은 새로 생긴 거죠.
01:39:48포스트 트랜스포머.
01:39:50그리고 그건 클래스예요.
01:39:51베이스 트랜스포머를 상속받은.
01:39:53좋아요.
01:39:53그 안에서.
01:39:54우리는 그냥 설명하는 거예요.
01:39:55게시물을 직렬화하는 방법을.
01:39:56그러니까 말하자면.
01:39:57헤이.
01:39:58이 속성들을 전부 다 골라.
01:40:01그리고.
01:40:02사용자에 대해서도.
01:40:03사용자도 변환해 줘.
01:40:04그러니까 본질적으로.
01:40:05제 말은, 이런 경우엔.
01:40:06변환은 꽤 간단할 겁니다.
01:40:08아마 그저 취하는 거겠죠.
01:40:11ISO 형식을요.
01:40:12날짜 형식이요.
01:40:13여기에 있는 날짜들이요.
01:40:14그리고 다른 원시 데이터는,
01:40:15담을 수 있죠.
01:40:16자바스크립트 객체 안에요.
01:40:17이렇게 말이죠.
01:40:18그러니까 기본적으로는,
01:40:19설명하는 겁니다.
01:40:20변환 방법을요.
01:40:21클래스를요.
01:40:21내용물을 포함하고 있을 수 있는,
01:40:23쉽게 변환되지 않을 수도 있는 내용을요.
01:40:25JSON으로요.
01:40:26어떻게 변환할지 말이죠.
01:40:27그걸 JSON으로요.
01:40:28그게 바로,
01:40:28트랜스포머가 할 일입니다.
01:40:30제가 이해하기로는요.
01:40:33네.
01:40:33그럼 여기서 포스트들을 설정하고,
01:40:34이제,
01:40:35뷰에서요.
01:40:40뷰에서요.
01:40:41페이지를 가져옵니다.
01:40:42좋아요.
01:40:43그냥 설정하는 겁니다.
01:40:43이런 식으로 페이지 속성을요.
01:40:44네.
01:40:45그냥 설정하는 거죠.
01:40:46속성 타입을요.
01:40:47타입스크립트 타입을요.
01:40:48Inertia 속성을 사용해서요.
01:40:51대신에요.
01:40:51일부 생성된 타입들 대신에요.
01:40:54아니면,
01:40:55AI가,
01:40:56그걸 추가했나요?
01:40:57제 생각엔,
01:40:58그건,
01:40:58표준 타입들인 것 같네요.
01:41:00아니,
01:41:00AI가 생성한 건 아니고요.
01:41:02그리고,
01:41:02그냥 이렇게 말하는 거죠.
01:41:03네.
01:41:03여기요.
01:41:04여기에,
01:41:04포스트 키가 있을 겁니다.
01:41:06그리고,
01:41:06그건,
01:41:07타입이죠.
01:41:08네.
01:41:08그러니까 그건,
01:41:09자동 생성된,
01:41:10타입입니다.
01:41:10우리의,
01:41:11포스트에 대한 타입이죠.
01:41:12그리고 제 생각에는요.
01:41:13이 타입은,
01:41:14생성되는 겁니다.
01:41:15우리가 어떻게,
01:41:15변환하느냐에 따라서요.
01:41:16포스트를요.
01:41:17그러니까 여기 포스트는,
01:41:20이건요.
01:41:23네.
01:41:24포스트 트랜스포머에서 오고 있죠.
01:41:25맞아요.
01:41:27좋아요.
01:41:28그리고 나서 그냥,
01:41:29여기서 렌더링하면 됩니다.
01:41:30그렇게 어렵지 않아요.
01:41:32알겠습니다.
01:41:35네.
01:41:36자 다시,
01:41:36공식 튜토리얼은 여기,
01:41:38엣지 뷰를 사용합니다.
01:41:39그래서요.
01:41:39저는 관성을 사용하고 있고요.
01:41:40그리고,
01:41:41보시다시피요.
01:41:41결국엔 그냥 리액트 앱입니다.
01:41:43결국엔요.
01:41:44거기서 우리는 또한,
01:41:46변환하고,
01:41:46우리의,
01:41:48속성을 보냅니다.
01:41:50네.
01:41:53단일 포스트를 표시하는 건,
01:41:54네.
01:41:55물론이죠.
01:41:55그러니까 이제,
01:41:55추가할 수 있습니다.
01:41:56다른,
01:41:56메서드를요.
01:41:58여기 메서드를요.
01:42:00우리의,
01:42:00컨트롤러에,
01:42:01왜냐하면,
01:42:02우리의,
01:42:02포스트,
01:42:03컨트롤러에,
01:42:03현재로서는,
01:42:04하나가 있거든요.
01:42:05인덱스,
01:42:05메서드가요.
01:42:05모든,
01:42:06포스트를,
01:42:07표시하는 거요.
01:42:08하지만 이제,
01:42:08우리는 원합니다.
01:42:08또한,
01:42:09메서드를 추가하길요.
01:42:09단일,
01:42:10포스트를,
01:42:10표시하는,
01:42:10방법을요.
01:42:11그래서,
01:42:11추가하는 겁니다.
01:42:12쇼,
01:42:12메서드를요.
01:42:12여기요.
01:42:13보이는 것처럼요.
01:42:14그리고 다시,
01:42:14이 이름들은,
01:42:16여러분이 정하기 나름입니다.
01:42:16나중에,
01:42:17연결할 때요.
01:42:18그것들을요.
01:42:18라우트,
01:42:19파일에서요.
01:42:19꼭,
01:42:19확인하셔야 합니다.
01:42:20그것들이 일치하는지,
01:42:20확인해야 합니다.
01:42:21하지만 잠시 후에,
01:42:21보게 될 겁니다.
01:42:23그래서 여기,
01:42:23파라미터들을,
01:42:24받고 있죠.
01:42:25왜냐하면 이제,
01:42:25우리는,
01:42:26동적,
01:42:26라우트를,
01:42:26가지고 있으니까요.
01:42:27그리고 다시,
01:42:28아실지도 모르겠네요.
01:42:28리액트나,
01:42:28뭐 그런 것들에서요.
01:42:29그게,
01:42:29라우트들이죠.
01:42:30콜론,
01:42:30콜론을 가진,
01:42:31콜론 파라미터요.
01:42:31파라미터요.
01:42:32라우트,
01:42:32이름에서,
01:42:33예를 들어,
01:42:33예를 들면요.
01:42:34혹은,
01:42:34달러,
01:42:35사인이요.
01:42:35파일,
01:42:35이름에서,
01:42:36그렇죠.
01:42:37그래서 여기,
01:42:38우리도 또한,
01:42:38받고 있습니다.
01:42:39동적,
01:42:39파라미터들을요.
01:42:40그리고 어떻게,
01:42:40등록하는지,
01:42:41보게 될 겁니다.
01:42:42그 라우트들을요.
01:42:43그리고 나서,
01:42:43예상합니다.
01:42:44가질 것을요.
01:42:44ID를,
01:42:45파라미터를요.
01:42:46그러니까 찾고 있는,
01:42:46포스트를요.
01:42:46포스트를요.
01:42:47특정,
01:42:48ID를 가진 포스트를요.
01:42:49원합니다.
01:42:49실패하길요.
01:42:50만약,
01:42:50못 찾는다면요.
01:42:51그래야 우리가,
01:42:52404 에러를 보여줄 수 있거든요.
01:42:53아니면 다른 에러라도요.
01:42:54제 생각에는요.
01:42:55원합니다.
01:42:55렌더링하는 것을요.
01:42:57뷰가 아니라요.
01:42:58대신에,
01:42:58우리는 원합니다.
01:42:59렌더링하려고 합니다.
01:43:05우리는
01:43:05가져오길
01:43:06Inertia를 원합니다.
01:43:09그리고 재사용하죠.
01:43:10포스트
01:43:11변환기를요.
01:43:12전달하기 위해
01:43:13우리 포스트를
01:43:14
01:43:17쇼 컴포넌트로
01:43:18예를 들면
01:43:18말이죠.
01:43:19그래서
01:43:20이제
01:43:20Inertia
01:43:21폴더 내의
01:43:21포스트
01:43:22폴더에
01:43:22우린
01:43:23가지고 있어야
01:43:23합니다.
01:43:24show.tsx나
01:43:24아니면
01:43:25detail.tsx
01:43:26같은 게 필요하죠.
01:43:26혹은
01:43:26그런 거면
01:43:27뭐든요.
01:43:28그리고
01:43:29이제
01:43:29그 안에
01:43:31우린
01:43:35우리
01:43:35우리
01:43:35포스트
01:43:36컴포넌트를
01:43:36가집니다.
01:43:38사실
01:43:40이름을 정하죠.
01:43:42Post라고.
01:43:45Post.
01:43:47이런 식으로요.
01:43:48그런 다음
01:43:49전과 마찬가지로
01:43:50제가
01:43:50재빨리
01:43:51가져오겠습니다.
01:43:55단일
01:43:55포스트를 얻었죠.
01:43:56여기요.
01:43:56배열이 아닙니다.
01:44:00이걸 선택하고
01:44:01그럼
01:44:01우린
01:44:02반환할 수
01:44:05있죠.
01:44:05물론이죠.
01:44:06포스트
01:44:06제목을요.
01:44:07이런 식으로요.
01:44:11안녕하세요.
01:44:12네,
01:44:12이런 식으로요.
01:44:13좋아요.
01:44:16여기서 뭘 하고 있는 거죠?
01:44:17그러니까
01:44:19그리고
01:44:19물론
01:44:19또한
01:44:20
01:44:21포스트
01:44:21요약도
01:44:21예를 들어서요.
01:44:22네,
01:44:23좋아요.
01:44:23이걸 가져오면
01:44:24음,
01:44:25잘 작동할 겁니다.
01:44:26이제
01:44:27라우트 폴더에 등록해야 합니다.
01:44:28그러니 이제
01:44:30추가할 수 있죠.
01:44:30새로운
01:44:31어,
01:44:32GET 라우트를요.
01:44:33슬래시
01:44:34슬래시
01:44:35포스트요.
01:44:35그런 다음
01:44:36제 생각엔
01:44:37이렇게 동적 라우트를
01:44:38등록하는 것 같아요.
01:44:39어디 봅시다.
01:44:41네,
01:44:42콜론 ID로요.
01:44:44아주 전형적이죠.
01:44:45이제
01:44:46말하자면
01:44:46있잖아요.
01:44:47우리 포스트 컨트롤러의
01:44:48show 메서드에
01:44:49관심이 있는 겁니다.
01:44:51왜냐면
01:44:52그 컨트롤러에서
01:44:53이 메서드 이름을
01:44:54show라고
01:44:54지었으니까요. 여기서 다른
01:44:56이름을 선택하면 여기도 바꿔야
01:44:58합니다. 하지만 전 show를
01:44:59쓰고 있으니까 잘 될 겁니다.
01:45:02그러니까
01:45:05어떤 링크가 있죠?
01:45:08오,
01:45:08상세 링크가 아니네요.
01:45:10하지만 posts/1을 더하면
01:45:13네,
01:45:14불러와지네요.
01:45:15스타일링은 엉망이지만요.
01:45:16여기 맨 아래를 보면
01:45:17
01:45:18콘텐츠를
01:45:19볼 수 있죠.
01:45:20그리고
01:45:21제목도요.
01:45:22마크다운 블로그 엔진이
01:45:24여기 있네요.
01:45:25스타일링은 완전히 깨졌지만요.
01:45:26그래도
01:45:27작동은 하네요.
01:45:28그 라우트를 볼 수 있어요.
01:45:31물론 스타일링은 고칠 수 있지만
01:45:32지금 당장 제일 중요한 건 아니죠.
01:45:35네, 그래요.
01:45:36뷰를 얻었네요.
01:45:37이미 다 해결된 거죠.
01:45:39명명된 라우트를 써서요.
01:45:40오, 재밌네요.
01:45:40네,
01:45:41지금은 URL을 하드코딩 중이에요.
01:45:44링크는 하나도 설정 안 했거든요.
01:45:47그래서
01:45:49라우트 정의에서 컨트롤러를 사용하면
01:45:51Adonis.js가 라우트 이름을 자동으로
01:45:54컨트롤러와 메서드 명을 기반으로
01:45:56생성해 줍니다. 그러니까 controllers.posts.index가
01:45:58있다면
01:45:59자동으로 posts.index라는 이름을 갖게 돼요.
01:46:02그러니 제 생각엔 가볼 수 있을 것 같아요.
01:46:05모든 포스트가 있는 포스트 인덱스 페이지로요.
01:46:08그리고 이런 앵커 태그 대신에
01:46:11사용할 수 있을 거예요.
01:46:12Link 컴포넌트를요.
01:46:13inertia.js react에서 오는 거죠.
01:46:17여기도요.
01:46:18그리고 잘 모르겠는데
01:46:19to 프로퍼티가 있나?
01:46:21아니면
01:46:21ref인가?
01:46:23그럼 이렇게 말할 수 있겠죠.
01:46:26routes.
01:46:29posts.show.
01:46:34이 방식은 아닌 것 같네요.
01:46:42posts.show.
01:46:49네,
01:46:49전 다른 접근 방식을 쓰고 있어요.
01:46:50이 Link 컴포넌트를 쓰고 싶거든요.
01:46:53다시.
01:46:55여기 어디에 이게 있나요?
01:46:57Link route.
01:46:59아, 알겠어요.
01:46:59그냥
01:47:01좋아요.
01:47:02그러니까 Link죠.
01:47:04Route.
01:47:08왜 route에서 에러가 나는 거죠?
01:47:11링크 생성.
01:47:12Link 컴포넌트는 내비게이션 링크를 만듭니다.
01:47:14아,
01:47:15임포트가 틀렸네요.
01:47:16어디서 가져와야 하는 건데.
01:47:20여기서요.
01:47:20Adonis 패키지요.
01:47:22이제 Route 지원을 받을 수 있죠.
01:47:24이제 posts뿐이네요.
01:47:33그냥 문자열입니다.
01:47:34그냥 문자열이에요.
01:47:35알겠어요.
01:47:35그러니까 그냥
01:47:37Route.
01:47:38알겠어요.
01:47:38이제 자동 완성이 좀 되네요.
01:47:40이제 params를 설정해야겠네요.
01:47:43Route params.
01:47:45posts.id요.
01:47:48아니요.
01:47:49post.id요.
01:47:53그럼 이건 객체여야겠네요.
01:47:57id는 post.id로 설정하고요.
01:48:00그러면 이제 모든 포스트마다 동적으로 다른 라우트가 생성될 겁니다.
01:48:03그리고 그 동적 자리 표시자를 포스트 ID로 채울 것이라고 생각합니다.
01:48:07그러니 이제 여기를 새로고침하면.
01:48:09여기가 아니고요.
01:48:10바로 여기죠.
01:48:14왜 업데이트가 안 됐죠?
01:48:16개발 서버를 재시작해야 하나요?
01:48:20아.
01:48:21파일을 저장하는 것도 아마 도움이 될 겁니다.
01:48:24네.
01:48:24이제 여기를 클릭해서 포스트로 이동할 수 있습니다.
01:48:28물론 스타일은 여전히 끔찍하지만요.
01:48:30그래도 작동은 합니다.
01:48:33그리고 당연히.
01:48:34여기서 라우트 이름을 사용하는 것의 장점은 경로를 변경하더라도
01:48:38링크를 변경할 필요가 없다는 것입니다.
01:48:40라우트 이름이 바뀌지 않는 한 말이죠.
01:48:42그래서 물론 아주 좋은 기능입니다.
01:48:44자.
01:48:45경로를 그렇게 자주 바꾸지는 않을 수도 있지만요.
01:48:47그래도.
01:48:48꽤 괜찮습니다.
01:48:52안녕하세요.
01:48:52키디 미(kiddie me)군요.
01:48:53네.
01:48:55라라벨에서 영감을 받은 것이 맞습니다.
01:48:57분명합니다.
01:48:58제 생각에 그들은 스스로를 '자바스크립트를 위한 라라벨'이라고 브랜드화하고 있는 것 같아요.
01:49:07직접 손으로 코딩해야 하나요?
01:49:09마치 어린아이 장난감 같네요.
01:49:12음.
01:49:17그래서.
01:49:18무언가를 배울 때는.
01:49:19확실히 직접 해봐야 합니다.
01:49:21손으로 직접 코드를 작성해 보는 것이죠.
01:49:23그냥 읽기만 해서는...
01:49:26저는 혼합해서 하는 편입니다.
01:49:28하지만 물론 라이브 스트림에서는.
01:49:29AI에게 다 맡겨버리면 별로 재미가 없죠.
01:49:30그래도 방금 전에 잠깐 사용하긴 했습니다.
01:49:33하지만 네.
01:49:36그렇지만요.
01:49:37배울 때는 확실히.
01:49:39직접 해봐야 합니다.
01:49:40읽기만 해서는.
01:49:44읽기만 하는 것으로는 학습이 잘 안 되거든요.
01:49:46이제.
01:49:474년 전처럼 코드를 전부 직접 짜야 한다고 말하려는 건 아닙니다.
01:49:50전혀 그렇지 않죠.
01:49:52하지만 최소한 기본적인 것들은요.
01:49:54느낌을 익히려면 말이죠.
01:49:56여기를 보면요.
01:49:56방금 들어오셨잖아요.
01:49:57저는 그냥 제가 쓴 것을
01:49:58그대로 복사하고 있었던 거죠.
01:49:59아니, 아니에요.
01:50:01음.
01:50:03코드.
01:50:03네.
01:50:03네.
01:50:03알아요.
01:50:04무슨 뜻인지 이해했어요.
01:50:05기분 나쁘지 않습니다.
01:50:06그냥.
01:50:07저는 코드를 작성하는 것이
01:50:09일반적으로 좋은 지점이라고 생각합니다.
01:50:10그래서 이에 대해 의견을 드리는 거예요.
01:50:12제 생각엔.
01:50:14코드를 작성하는 것은 여전히 학습에 좋습니다.
01:50:18하지만.
01:50:19비록 농담이었지만요.
01:50:21혹은 인용구였더라도.
01:50:24분명.
01:50:26이건.
01:50:26확실히 변하고 있습니다.
01:50:28그리고 아까 질문을 받았었는데.
01:50:32새로운 강좌에 대한 질문이었죠.
01:50:33한 가지 문제는.
01:50:34제가 알아내려고 하는 것은
01:50:35어떻게 하면
01:50:37지금 이 시점에 기술을 가장 잘 배울 수 있는가 하는 것입니다.
01:50:41여전히 중요한가에 대해서요.
01:50:42네, 그렇다고 말하고 싶네요.
01:50:43분명 여전히 중요합니다.
01:50:45하지만 네.
01:50:46바로 그 점이죠.
01:50:47얼마나 직접 손으로 작성하는가?
01:50:48얼마나 복사해서 붙여넣는가?
01:50:50또 얼마나 AI에게 설명하거나 생성하게 할 것인가?
01:50:52그게.
01:50:53그냥 두서없이 말하고 있네요.
01:50:54그냥 주절거리고 있어요.
01:50:54하지만 이건 확실히 변하고 있는 부분입니다.
01:50:57이것은 아도니스 JS(Adonis JS)입니다.
01:51:00그러니까.
01:51:00한마디로 자바스크립트용 라라벨(Laravel) 같은 거죠.
01:51:09좋아요.
01:51:09그래서.
01:51:14네.
01:51:15댓글.
01:51:16그건 하지 않을 것 같네요.
01:51:1810분 뒤에 가봐야 해서요.
01:51:20그러니 여기는 일단 이대로 놔두고.
01:51:22남은 몇 가지를 잠깐 살펴볼게요.
01:51:28여기 페이지들이요.
01:51:29어쨌든 이 가이드에는 별로 남은 게 없어요.
01:51:31꽤 잘 진행해 왔으니까요.
01:51:33폼과 유효성 검사.
01:51:35자, 여기 아이디어는 이겁니다.
01:51:37물론 컨트롤러 함수도 가질 수 있다는 것이죠.
01:51:40호출되는 컨트롤러 메서드 말입니다.
01:51:43POST 요청 시에 말이죠.
01:51:45그리고 그건 여기에서 볼 수 있습니다.
01:51:48사용자 컨트롤러가 없나요?
01:51:52사용자.
01:51:54이거.
01:51:54새로운 계정 컨트롤러요.
01:51:56여기요.
01:51:57스토어(store).
01:51:57그게.
01:51:59컨트롤러 메서드죠.
01:52:00호출되는 메서드요.
01:52:02POST 요청 시에요.
01:52:04이거요.
01:52:05그리고.
01:52:06따라서.
01:52:07그냥 일반적인 메서드입니다.
01:52:08하지만 그 안에서 벌써 볼 수 있죠.
01:52:10validateUsing 메서드를 사용할 수 있습니다.
01:52:13메서드요.
01:52:13그리고 유효성 검사기를 정의하죠.
01:52:14결국.
01:52:16작은 유틸리티 객체일 뿐입니다.
01:52:18여기 있는 바인(vine) 도구로 생성된 거요.
01:52:21받아들일 데이터의 규칙을 정의할 수 있죠.
01:52:22어떤 종류의 데이터를 수락할지 말이에요.
01:52:24그리고 나서.
01:52:24오류가 자동으로 생성될 겁니다.
01:52:26만약.
01:52:27유효성 검사 기준이 충족되지 않으면요.
01:52:29그러니까 라라벨의 전체 아이디어는.
01:52:30그리고 아도니스의 아이디어는.
01:52:30그들이 많은 일을
01:52:31당신을 대신해서 해준다는 겁니다.
01:52:33그래서 당신은.
01:52:33논리만 정의하면 되죠.
01:52:34꽤 높은 수준에서 말이죠.
01:52:36반면에.
01:52:37넥스트.
01:52:37JS.
01:52:37등에서는.
01:52:38종종 정말 구체적인 것까지 직접 해야 합니다.
01:52:41당연히.
01:52:41직접 유효성 검사 라이브러리를 가져와야 하고요.
01:52:43그런 모든 재미있는 일들을요.
01:52:52그래서 여기서 우리가 하고 있는 것은.
01:52:54라우트를 등록하는 것입니다.
01:52:56이미 봤던 내용이죠.
01:52:59이 경우 새로운 뷰를 만들고요.
01:53:00그리고 여기서는 포럼을 만들고 있습니다.
01:53:02이건 다들 특정 에지(Edge) 문법입니다.
01:53:05코드 조각들이죠.
01:53:06하지만 물론.
01:53:08여기서도 확인할 수 있습니다.
01:53:12어디 있죠?
01:53:13로그인.
01:53:14네.
01:53:14Inertia를 사용하고 있다면 확인할 수 있죠.
01:53:17여기 이 폼 컴포넌트가 있습니다.
01:53:19이건 Adonis.js Inertia 패키지에서 오는 것입니다.
01:53:22또한 라우트와도 연결되어 있죠.
01:53:24트리거되어야 하는.
01:53:25폼이 제출되면요.
01:53:26바로 그 스토어 라우트입니다.
01:53:27방금 본 것이요.
01:53:28아니면 그 스토어 컨트롤러 액션 말이죠.
01:53:30결국.
01:53:31그 라우트에 의해 호출되는 것이죠.
01:53:33그리고 데이터는.
01:53:34아마도.
01:53:36요청에 담겨서.
01:53:37자동으로 백엔드에 제출될 겁니다.
01:53:39물론 아주 편리한 거죠.
01:53:40그 코드를 직접 작성할 필요가 없으니까요.
01:53:42아니면 AI에게 작성해 달라고 할 필요도 없고요.
01:53:45에지를 사용하고 있다면요.
01:53:46폼 입력을 이렇게 렌더링할 수 있습니다.
01:53:48그리고 아마도.
01:53:50이게 이해하기가 좀 더 쉽고.
01:53:51읽기도 더 편하다고 봅니다.
01:53:56그리고 유효성 검사기를 만들 수 있습니다.
01:53:57아까 전에 하나 봤었죠.
01:53:58그 바인(vine) 도구를 이용해서요.
01:54:00단순히 정의만 하면 됩니다.
01:54:01받으려는 데이터에 대해
01:54:02어떤 규칙을 가질지 말이에요.
01:54:04그러니까 최소 길이,
01:54:05최대 길이,
01:54:06데이터 타입,
01:54:07그런 것들을요.
01:54:08등등이요.
01:54:10스토어 메서드를 가져오고,
01:54:11그다음에 다시,
01:54:11ORM을 사용할 수 있죠.
01:54:13우리 클래스를 사용해서,
01:54:14데이터를 저장하기 위해 정적 create 메서드를 호출합니다.
01:54:16데이터를 저장하려면 말이죠.
01:54:19댓글도,
01:54:20똑같이 하면 됩니다.
01:54:23사용자와 연결할 수도 있죠.
01:54:24ID를 통해서요.
01:54:26그리고 사용자를 얻게 됩니다.
01:54:28인증을 통해서요.
01:54:30속성이죠.
01:54:31자동으로 얻게 되는 속성입니다.
01:54:32다시 말하지만,
01:54:33이건 마치,
01:54:33Adonis가 자기 할 일을 하는 거죠.
01:54:35알아서 인증을 처리해 주는 겁니다.
01:54:37미들웨어 덕분이죠.
01:54:39등록되어 있는,
01:54:40그렇죠.
01:54:40그래서 라우트에서,
01:54:40몇 가지,
01:54:41인증 미들웨어를 가져옵니다.
01:54:42예를 들어서요.
01:54:43여기에 있는 모든 라우트는,
01:54:44인증이 필요합니다.
01:54:46그리고 저기에 있는 라우트들도,
01:54:48그러므로,
01:54:48역시 마찬가지로,
01:54:51이 인증 속성도 얻게 됩니다.
01:54:53속성 말이죠.
01:54:54사용할 수 있는,
01:54:55데이터를 얻기 위해,
01:54:56현재,
01:54:57인증된 사용자에 대한 정보를요.
01:54:58아니면,
01:54:59사용자를 로그아웃시킬 때도,
01:55:00이런 경우처럼요.
01:55:01기타 등등.
01:55:02그게 다입니다.
01:55:03꽤 편리하죠,
01:55:04일단 설정만 되면요.
01:55:06최소한 제 생각에는요.
01:55:07이전에,
01:55:08제가 이걸 다뤄본 건 아니지만요.
01:55:09그래도요.
01:55:11배웠습니다.
01:55:11당신으로부터 많은 걸요.
01:55:12그리고 그 원칙들을,
01:55:14코드에 전달했죠.
01:55:14코드 같은,
01:55:15고품질의,
01:55:16그래서 당신의 강의는,
01:55:16여전히 가치가 있어요.
01:55:17좋은 프롬프팅을 위해서요.
01:55:18네.
01:55:18그리고 제가 탐구 중인 한 가지는,
01:55:20어떻게,
01:55:20할 수 있을지,
01:55:22향후 강좌들에,
01:55:23요소를 넣는 거죠,
01:55:24에이전트 기술 같은,
01:55:25혹은 추가 문서들을요.
01:55:26제공할 수 있는,
01:55:27에이전트에게,
01:55:28도움이 되도록,
01:55:31더 나은 코드를 작성하도록요.
01:55:32물론 물론,
01:55:33보장할 수는 없겠지만요.
01:55:34왜냐하면 에이전트들은,
01:55:37항상 제멋대로,
01:55:38자기 마음대로 할 수 있으니까요.
01:55:39기타 등등.
01:55:40하지만 그건,
01:55:40저도 탐구 중인,
01:55:41분야죠.
01:55:42그리고 제가 선호하는 건,
01:55:43Adonis인가 View 3인가?
01:55:45Adonis는,
01:55:46방금 시작했을 뿐이고,
01:55:47이제 막 시작했으니까요.
01:55:48전에는 전혀 사용해 본 적이,
01:55:49없었고요.
01:55:50그리고 View 3는,
01:55:52진정한 대안은,
01:55:52아니에요.
01:55:53Adonis는,
01:55:54풀스택이니까요.
01:55:55반면 View는,
01:55:55클라이언트 사이드,
01:55:57프런트엔드니까요.
01:55:58Nuxt와 함께라면,
01:55:59풀스택이 될 수도 있죠.
01:56:00다시 말하지만,
01:56:00하지만,
01:56:01그래도 여전히,
01:56:02철학이 다릅니다.
01:56:03Adonis는,
01:56:04모든 기능을 포함하고 있으니까요.
01:56:06그래서,
01:56:06그런 것들,
01:56:07인증 같은 건,
01:56:08내장되어 있죠.
01:56:09반면 Nuxt는,
01:56:11Next와,
01:56:11Next와 마찬가지로,
01:56:12직접,
01:56:13라이브러리를,
01:56:14가져와야 하죠.
01:56:15결국,
01:56:16잘 모르겠네요.
01:56:17제가 뭘 선호하는지,
01:56:17전,
01:56:18Adonis 방식이 좋고,
01:56:19분명히,
01:56:21가치가 있다고,
01:56:22생각합니다,
01:56:23만약 당신이,
01:56:25풀스택 애플리케이션을,
01:56:27구축한다면요.
01:56:28이제,
01:56:28저는,
01:56:29너무 익숙해져서,
01:56:30Next JS에,
01:56:31그러니까,
01:56:31T3 스택에,
01:56:32그리고 그런 것들에,
01:56:33너무 익숙해서,
01:56:34글쎄요,
01:56:35잘 모르겠네요,
01:56:36Adonis로 바꿀지,
01:56:37단지,
01:56:37아마도,
01:56:38더 빠를 테니까요,
01:56:39다른,
01:56:40기술 스택들보다,
01:56:41하지만,
01:56:41그래도 다시 말하지만,
01:56:43좋은 이유들이,
01:56:44있어요,
01:56:45가지는 것 같은,
01:56:46그럼,
01:56:46더 적은,
01:56:47외부,
01:56:48의존성을요,
01:56:49Adonis 외에도요,
01:56:50뭐 이런 것들 말이죠,
01:56:51그래서,
01:56:51아마 계속,
01:56:52가지고 놀면서,
01:56:53계속,
01:56:54해볼 것 같네요,
01:56:55그리고,
01:56:55평가해 봐야죠,
01:56:56어디가 유용한지,
01:56:57제게,
01:56:58어디가 그렇지 않은지,
01:56:58더 많이 만들고 싶다면요,
01:57:00그걸로요,
01:57:02네,
01:57:03그게,
01:57:03본질적으로는,
01:57:04계획입니다,
01:57:05그리고 제 생각에,
01:57:07글쎄,
01:57:07아니,
01:57:08아니에요,
01:57:08그래도,
01:57:09스타일링은 딱히,
01:57:10신경 안 쓰여요,
01:57:10그건 그냥 CSS니까,
01:57:12하지만 권한 부여는,
01:57:13한번 살펴보고 싶네요,
01:57:16또 다른 패키지가 있거든요,
01:57:18설치할 수 있는,
01:57:19Adonis의,
01:57:20생태계 일부분이죠,
01:57:21밸런서 패키지요,
01:57:22그게 있어서,
01:57:24이름을 정하는 거죠,
01:57:25이 챕터의 이름은,
01:57:27제 생각엔,
01:57:27그건,
01:57:28책임이 있어요,
01:57:29사람들을 차단하는,
01:57:30허용되지 않은,
01:57:31뭔가를 하도록,
01:57:33물론,
01:57:35권한 부여는,
01:57:36인증과는 다르게,
01:57:37이것은,
01:57:39사용자를 차단하는,
01:57:40특정 행동을 못 하게요,
01:57:41허용되지 않은,
01:57:42할 수 없는,
01:57:43예를 들어,
01:57:44사용자가 로그인되어 있을 순 있지만,
01:57:46물론,
01:57:46수정할 순 없어야 하죠,
01:57:47게시물을 수정하는 것과 같은
01:57:48다른 사용자의 게시물을
01:57:49말이죠.
01:57:51그러니
01:57:52인증만으로는
01:57:52충분하지 않습니다.
01:57:53종종 필요한 것은
01:57:54인가입니다.
01:57:55게다가
01:57:57바로 그 지점에서
01:57:57이 Bouncer 패키지가
01:57:58유용해 보입니다.
01:58:01왜냐하면
01:58:01이른바
01:58:02정책을 만들 수
01:58:03있기 때문입니다.
01:58:04살펴보자면,
01:58:05정책은 하나의 클래스입니다.
01:58:07확장하는 클래스죠.
01:58:07클래스인데,
01:58:08Adonis의
01:58:09Bouncer
01:58:10패키지에서 가져온 것이죠.
01:58:11그리고 나서 정의할 수 있습니다.
01:58:13자,
01:58:13기본적으로
01:58:15권한을 여기서 정의할 수 있어요.
01:58:16예를 들어
01:58:17사용자 ID가
01:58:18수정을 허용하는지
01:58:20일치하는지를 보는 거죠.
01:58:21게시물 작성자의
01:58:22ID와 말이죠.
01:58:23수정하려는 게시물의요.
01:58:24삭제도 마찬가지고요.
01:58:26그리고 그런 다음에는 아마
01:58:27해당 정책을 적용할 수 있겠죠.
01:58:29한번 봅시다.
01:58:30우리...
01:58:30우리에겐 정책이 있습니다.
01:58:34컨트롤러 메서드를 추가하는 것 말이죠.
01:58:43어디에 적용하는지 볼까요?
01:58:44아, 그래요.
01:58:45여기네요.
01:58:45자,
01:58:46우리 컨트롤러에서
01:58:48우리는
01:58:48여기,
01:58:49새로운 메서드를 추가했어요.
01:58:50편집(edit) 메서드죠.
01:58:51이건 분명히 실행되어야 합니다.
01:58:52만약 당신이
01:58:52뭔가를,
01:58:53폼을 제출하려고 하거나
01:58:54게시물을 수정하고 싶을 때 말이죠.
01:58:56아니면
01:58:57페이지를 불러오고 싶을 때,
01:58:58게시물 수정 페이지를 말이죠.
01:58:59이제 보니까 그렇네요.
01:59:01이 메서드에서 우리는
01:59:02수정하고 싶은
01:59:03게시물을 찾고 있습니다.
01:59:04수정하려는 게시물을요.
01:59:05그런데,
01:59:06뷰를 반환하기 전에,
01:59:08이 Bouncer 패키지가
01:59:09우리에게 기능을 제공합니다.
01:59:11정책을 불러올 수 있는,
01:59:12우리가 적용하려는
01:59:13정책을 말이죠.
01:59:14그리고 기본적으로
01:59:15정책을 통해 확인합니다.
01:59:16사용자가,
01:59:17이것을 수정하려는 사용자가
01:59:19허용되는지 확인하는 거죠.
01:59:23이해되시죠?
01:59:25좋습니다.
01:59:27자, 한번 보죠.
01:59:29챗.
01:59:30AI는 앱을 만들 수 있어요.
01:59:31같은 라이브러리 내에서도
01:59:3210가지 방식으로요.
01:59:33그러니 아마
01:59:34프로그래밍 패러다임에,
01:59:35코드를 구성하는 방법에 집중하세요.
01:59:37네.
01:59:38물론입니다.
01:59:38저는 생각합니다.
01:59:39이것들이 중요한 기술이라고요.
01:59:41개발자들에게나
01:59:42AI에게나 말이죠.
01:59:44문서를 통해서,
01:59:45기술을 통해서,
01:59:46당신이 가르치는
01:59:47특정한 패턴을 통해서요.
01:59:48당신이 알고 있는
01:59:49기본기 같은 것들이요.
01:59:50그런 거 말입니다.
01:59:51그래서 제가
01:59:52계획하고 있는 겁니다.
01:59:53프로그래밍 기초,
01:59:54시스템 설계,
01:59:55그리고 그 모든
01:59:55재미있는 것들에 대한 강좌를요.
01:59:57더 나은 선택이 있을 때,
01:59:58백엔드 웹 API를 할 때는
02:00:00C# 같은
02:00:01강타입 언어가 더 낫죠.
02:00:02그리고
02:00:03잘 모르겠네요.
02:00:03왜 여기서 검열됐는지 말이죠.
02:00:05아니면 Node.js에 의존하거나,
02:00:06TypeScript를 쓰죠.
02:00:07타입이 삭제되기도 하고요.
02:00:08NPM 패키지에는 수많은 취약점이 있죠.
02:00:09그러니까 결국,
02:00:12백엔드 언어에 관해서라면
02:00:13그것만으로도 아마 한 시간은
02:00:15이야기할 수 있을 거예요.
02:00:16그럴 시간은 없지만요.
02:00:17하지만
02:00:18Node.js의
02:00:20큰 장점은
02:00:21그리고 전체 JavaScript
02:00:22생태계의 장점은
02:00:23물론
02:00:24AI가 정말 잘 안다는 것이죠.
02:00:24개발에 AI를 사용할 계획이라면
02:00:26사용할 수 있는 패키지가 정말 많죠.
02:00:28하지만 물론
02:00:28공급망 공격 같은
02:00:30거대한 단점이 있죠.
02:00:31그리고 다른 것들도요.
02:00:32물론이죠.
02:00:33다른 언어들도
02:00:34더 나은 성능을
02:00:35제공할 수 있습니다.
02:00:36저는 분명히
02:00:38사용을 고려할 거예요.
02:00:39Go나 Rust, 혹은
02:00:41C# 같은 것들을요.
02:00:41관심 있는 게 뭐든
02:00:42백엔드 개발에
02:00:42쓰면 좋겠죠.
02:00:44하지만 말하고 싶은 건
02:00:45제 경험상
02:00:46TypeScript를 쓰면
02:00:47높은 속도를
02:00:48낼 수 있다는 겁니다.
02:00:49왜냐하면 AI가
02:00:50타입스크립트를 쓸 때입니다.
02:00:51물론 저도
02:00:52잘 알고 있고요.
02:00:53그래서 저는
02:00:55AI가 잘못된 방향으로
02:00:56나아가는 부분을
02:00:56빨리 캐치할 수 있죠.
02:00:57그런 면에서
02:00:58분명히 장점이 있습니다.
02:00:59다른 백엔드 언어를
02:01:01사용하는 것도요.
02:01:02전부 JavaScript일
02:01:02필요는 없죠.
02:01:04왜 NestJS가
02:01:05더 인기 있는지
02:01:06정말 모르겠네요.
02:01:06절대 완전히
02:01:06이해하지 못했어요.
02:01:07왜 Adonis가
02:01:08그렇게 니치한(틈새) 시장에
02:01:09머물러 있는지 말이죠.
02:01:10이렇다 할
02:01:11설명은 없습니다.
02:01:12모든 걸 자바스크립트로 할 필요는 없죠.
02:01:14왜 NestJS가 더 인기가 많을까요?
02:01:16흥미로운 시간이었습니다.
02:01:17정말 재미있었어요.
02:01:19Adonis를
02:01:19깊이 파보는 것도
02:01:22정말 흥미로웠어요.
02:01:23여러분도 즐거우셨길 바랍니다.
02:01:24그게,
02:01:24음.
02:01:25돌아오려 합니다.
02:01:26다음 주는
02:01:28참석하지 못할 것 같네요.
02:01:29그다음 주는
02:01:30상황을 봐야겠어요.
02:01:31하지만
02:01:32항상 Discord에
02:01:32공지합니다.
02:01:34아직 멤버가 아니라면
02:01:35[akadamind.com/community에](https://www.google.com/search?q=https://akadamind.com/community%EC%97%90)
02:01:36방문해서
02:01:37Discord에 참여하세요.
02:01:39거기에 공지사항을 올립니다.
02:01:40아니면 물론
02:01:40그냥 구독하고
02:01:41팔로우하시면
02:01:42소식을 들을 수 있을 겁니다.
02:01:43그럼 목요일에 뵙죠.
02:01:45오후 5시,
02:01:46중앙 유럽
02:01:47서머타임 기준으로요.
02:01:48보통 지금처럼
02:01:48방송을 마칩니다.
02:01:49그럼 모두
02:01:50감사드리고요.
02:01:51좋은 하루 되세요.
02:01:51저녁이든,
02:01:53아침이든,
02:01:54지금 어디에 계시든 말이죠.
02:01:54앞으로도 계속 뵙길 바랍니다.
02:01:56그럼,
02:01:56다들 함께해 주셔서 감사합니다.
02:01:57모두
02:01:58감사합니다.
02:01:59아,
02:02:00잠시만요,
02:02:01몇 가지
02:02:03더 말하고 싶은 게 있었는데,
02:02:04어쨌든
02:02:05함께해 주셔서 정말 고마워요.
02:02:06다들 좋은 시간 보내시고
02:02:07다음에 또 뵙죠.
02:02:09진짜로,
02:02:09정말 고마워요.
02:02:11다들 행복하세요.
02:02:13안녕!
02:02:13안녕!
02:02:14다들 안녕!
02:02:15다음에 또 봐요.
02:02:17안녕!
02:02:17정말 감사합니다.

Key Takeaway

Adonis.js는 외부 의존성을 최소화하고 인증 및 ORM 등 핵심 기능을 내장하여 안정적인 풀스택 타입스크립트 애플리케이션을 구축할 수 있는 라라벨 스타일의 프레임워크입니다.

Highlights

  • Adonis.js는 인증, ORM, 파일 업로드, 속도 제한 등 백엔드 필수 기능을 내장한 '배터리 포함' 타입스크립트 풀스택 프레임워크입니다.

  • 프론트엔드 구축 시 하이퍼미디어(Edge 템플릿), Inertia.js를 통한 React/Vue 통합, 순수 REST API 서버 모드 등 세 가지 핵심 접근 방식을 지원합니다.

  • 외부 라이브러리 의존성을 최소화하여 요즘처럼 빈번한 공급망 공격 위협으로부터 상대적으로 안전한 백엔드 환경을 제공합니다.

  • Lucid ORM을 내장하여 원시 SQL 쿼리 없이도 클래스 기반으로 데이터베이스 테이블과 관계를 설정하고 관리할 수 있습니다.

  • 서버 사이드 렌더링(SSR) 기능을 제공하여 SEO(검색 엔진 최적화) 문제를 해결하고 클라이언트 사이드 성능을 최적화할 수 있습니다.

Timeline

Adonis.js 프레임워크의 정체성과 장점

  • Adonis.js는 자바스크립트 생태계의 '라라벨'과 같은 배터리 포함 풀스택 프레임워크입니다.
  • 인증, 캐싱, 속도 제한, ORM 등이 외부 의존성 없이 프레임워크 내부에 이미 구현되어 있습니다.
  • 타입스크립트를 기본 지원하며 백엔드 우선 철학을 바탕으로 안정적인 애플리케이션 유지가 가능합니다.

Adonis.js는 수많은 개별 라이브러리를 조합해야 하는 현대의 복잡한 개발 방식과 대조적으로, 프레임워크 단에서 완성된 빌딩 블록을 제공합니다. 이는 공급망 공격 위협을 낮추고 의존성 관리의 피로도를 줄여줍니다. 특히 Next.js와 같은 방식보다 더 많은 백엔드 기능을 기본으로 갖추고 있어 대규모 풀스택 앱 구축에 적합합니다.

프론트엔드 구축을 위한 세 가지 아키텍처

  • 하이퍼미디어 방식은 Edge 템플릿 엔진과 Alpine.js를 결합해 서버에서 완전한 HTML을 생성합니다.
  • Inertia.js를 사용하면 React를 프론트엔드로 활용하면서도 백엔드 컨트롤러와 직접 데이터를 공유할 수 있습니다.
  • REST API 모드를 선택하면 백엔드 로직에만 집중하는 순수 API 서버로 활용 가능합니다.

Adonis.js는 동일한 컨트롤러 구조 내에서 뷰 레이어를 다르게 설정할 수 있는 유연성을 제공합니다. 고도의 인터랙티브한 리액트 앱이 필요 없는 블로그와 같은 단순 프로젝트의 경우 Edge 템플릿 엔진을 활용한 하이퍼미디어 방식이 복잡성을 획기적으로 낮춰줍니다.

데이터베이스 설정과 ORM 활용법

  • Lucid ORM을 사용하여 데이터베이스 스키마와 모델 관계를 클래스 기반으로 정의합니다.
  • 마이그레이션 파일과 시더(Seeder)를 통해 데이터베이스 구조 변경과 초기 테스트 데이터 채우기를 체계적으로 관리합니다.
  • 사용자 모델과 포스트 모델 간의 일대다 관계를 데코레이터 선언으로 간편하게 설정합니다.

데이터베이스 관리는 에이스(Ace) 명령어 세트를 통해 자동화됩니다. 모델과 마이그레이션을 생성하고 외래 키 관계를 설정하는 과정은 라라벨과 유사한 프로그래밍 방식을 따르며, 타입 안전성을 보장하는 스키마 업데이트가 자동으로 이루어집니다.

보안 정책 적용 및 권한 부여

  • Bouncer 패키지는 특정 사용자가 특정 자원에 접근하거나 수정할 권한이 있는지 인가(Authorization)를 확인합니다.
  • 정책 클래스를 정의하여 게시물 소유자와 현재 사용자의 ID를 비교하는 등 정교한 권한 제어가 가능합니다.
  • 컨트롤러 메서드 수준에서 Bouncer를 호출하여 인가되지 않은 사용자의 접근을 차단합니다.

인증(Authentication)만으로는 충분하지 않은 실제 웹 서비스 환경에서 Bouncer는 보안 정책을 클래스 단위로 관리하게 해줍니다. 이를 통해 특정 사용자가 타인의 게시물을 수정하거나 삭제하지 못하도록 하는 인가 로직을 직관적이고 중앙화된 방식으로 구현할 수 있습니다.

Community Posts

No posts yet. Be the first to write about this video!

Write about this video