nhcodingstudio
@nhcodingstudio
Students
1,687
Reviews
106
Course Rating
4.8
안녕하세요, 우리동네코딩 스튜디오에 오신 것을 환영합니다!
우리동네코딩 스튜디오는 카네기 멜론, 워싱턴, 토론토, 워터루 등 북미의 주요 대학에서 컴퓨터공학을 전공하고, Google, Microsoft, Meta 등 글로벌 IT 기업에서 실무 경험을 쌓은 개발자들이 함께 만든 교육 그룹입니다.
처음에는 미국과 캐나다의 컴퓨터공학 전공자들끼리 함께 공부하며 성장하고자 만든 스터디 모임에서 시작되었습니다. 각기 다른 대학, 다른 시간대에 있었지만 함께 문제를 해결하고 서로에게 배운 그 시간은 매우 특별했고, 자연스럽게 이런 생각이 들었습니다.
“우리가 공부하던 이 방식, 그대로 다른 사람에게도 전하면 어떨까?”
그 물음이 바로 우리동네코딩 스튜디오의 출발점이었습니다.
현재는 약 30명의 현직 개발자와 컴퓨터공학 전공 대학생들이 각자의 전문 분야를 맡아, 입문부터 실전까지 아우르는 커리큘럼을 직접 설계하고 강의합니다. 단순한 지식 전달을 넘어, 진짜 개발자의 시선으로 배우고 함께 성장할 수 있는 환경을 제공합니다.
“진짜 개발자는, 진짜 개발자에게 배워야 합니다.”
저희는 웹 개발의 전 과정을 처음부터 끝까지 체계적으로 다루되, 이론에 머무르지 않고 실습과 실전 중심의 피드백을 통해 실력을 키워드립니다.
수강생 한 사람, 한 사람의 성장을 함께 고민하고 이끌어가는 것이 우리의 철학입니다.
🎯 우리의 철학은 분명합니다.
"진정한 배움은 실천에서 오고, 성장은 함께할 때 완성된다."
개발을 처음 시작하는 입문자부터, 실무 능력을 키우고 싶은 취업 준비생, 진로를 탐색 중인 청소년까지.
우리동네코딩 스튜디오는 모두의 출발점이자, 함께 걷는 든든한 동반자가 되고자 합니다.
이제, 혼자 고민하지 마세요.
우리동네코딩 스튜디오가 여러분의 성장을 함께하겠습니다.
Welcome to Neighborhood Coding Studio!
Neighborhood Coding Studio was founded by a team of developers who studied computer science at top North American universities such as Carnegie Mellon, the University of Washington, the University of Toronto, and the University of Waterloo, and went on to gain hands-on experience at global tech companies like Google, Microsoft, and Meta.
It all began as a study group formed by computer science students across the U.S. and Canada, created to grow together by sharing knowledge, solving problems, and learning from one another.
Though we were attending different schools in different time zones, the experience was so meaningful that it led us to one simple thought:
“What if we shared this way of learning with others?”
That thought became the foundation of Neighborhood Coding Studio.
Today, we are a team of around 30 active developers and computer science students, each taking responsibility for their area of expertise—designing and delivering a curriculum that spans from foundational knowledge to real-world development.
We’re not just here to teach—we’re here to help you see through the lens of real developers and grow together.
“To become a real developer, you must learn from real developers.”
Our courses take you through the entire web development journey—from start to finish—focused on hands-on practice, real-world projects, and practical feedback.
We care deeply about each learner’s growth and are committed to supporting your path every step of the way.
🎯 Our philosophy is simple but powerful:
"True learning comes from doing, and true growth happens together."
Whether you're just getting started, preparing for your first job, or exploring your future in tech,
Neighborhood Coding Studio is here to be your launchpad—and your trusted companion on the journey.
You don’t have to do it alone.
Let Neighborhood Coding Studio walk with you toward your future in development.
Courses
Reviews
kimsh322
·
Next.js Master Class: Part 3 - Completing Real-World Architecture (Server-Client Synchronization, Extreme Optimization, Full-Stack Security)Next.js Master Class: Part 3 - Completing Real-World Architecture (Server-Client Synchronization, Extreme Optimization, Full-Stack Security)juho07301208
·
React Master Class: Part 1 - Understanding the Essence of Rendering and Design Through MissionsReact Master Class: Part 1 - Understanding the Essence of Rendering and Design Through Missionsipeul696614
·
Next.js Master Class: Part 2 - Full-Stack Architecture and Deep Dive into Frameworks (Server Actions, Cache Revolution, Advanced Routing)Next.js Master Class: Part 2 - Full-Stack Architecture and Deep Dive into Frameworks (Server Actions, Cache Revolution, Advanced Routing)fkdlsl90887355
·
Next.js Master Class: Part 1 Learning the Essence of App Router and Rendering Design through MissionsNext.js Master Class: Part 1 Learning the Essence of App Router and Rendering Design through Missionsipeul696614
·
Next.js Master Class: Part 1 Learning the Essence of App Router and Rendering Design through MissionsNext.js Master Class: Part 1 Learning the Essence of App Router and Rendering Design through Missions
Posts
Q&A
[실습] 80강 없음
안녕하세요, icoon2님! 우리동네코딩 스튜디오입니다.해당 섹션의 수업 자료와 관련하여 혼선을 드려 죄송합니다.본래 76. 전문성 더하기: 놓치면 아쉬운 관련 기술들 섹션에서는 Better Auth를 비롯하여 Next.js 생태계에서 주목받는 최신 관련 기술들을 소개해 드리려고 준비했었습니다.하지만 현재 Next.js와 관련 라이브러리들의 트렌드가 워낙 빠르게 변화하고 있다 보니, 자칫 고정된 자료를 제공해 드리는 것이 수강생분들께 오히려 버전 불일치나 기술적 혼동을 드릴 여지가 있다고 판단하였습니다.이에 따라 수강생분들께 더욱 정확하고 최신의 정보를 전달하기 위해, 고민 끝에 해당 섹션을 커리큘럼에서 제외하게 되었습니다. 기대를 가지고 기다려 주셨을 텐데 양해를 부탁드립니다.대신 커뮤니티나 다른 섹션을 통해 더 안정적이고 검증된 기술들을 지속적으로 업데이트해 드릴 수 있도록 노력하겠습니다.강의를 수강해 주셔서 진심으로 감사드리며, 학습 중 다른 궁금한 점이 생기면 언제든 질문 남겨주세요!감사합니다.우리동네코딩 스튜디오 드림
- 0
- 2
- 52
Q&A
76. 전문성 더하기: 놓치면 아쉬운 관련 기술들 수업 자료가 없어요.
안녕하세요, icoon2님! 우리동네코딩 스튜디오입니다.해당 섹션의 수업 자료와 관련하여 혼선을 드려 죄송합니다.본래 '76. 전문성 더하기: 놓치면 아쉬운 관련 기술들' 섹션에서는 Better Auth를 비롯하여 Next.js 생태계에서 주목받는 최신 관련 기술들을 소개해 드리려고 준비했었습니다.하지만 현재 Next.js와 관련 라이브러리들의 트렌드가 워낙 빠르게 변화하고 있다 보니, 자칫 고정된 자료를 제공해 드리는 것이 수강생분들께 오히려 버전 불일치나 기술적 혼동을 드릴 여지가 있다고 판단하였습니다.이에 따라 수강생분들께 더욱 정확하고 최신의 정보를 전달하기 위해, 고민 끝에 해당 섹션을 커리큘럼에서 제외하게 되었습니다. 기대를 가지고 기다려 주셨을 텐데 양해를 부탁드립니다.대신 커뮤니티나 다른 섹션을 통해 더 안정적이고 검증된 기술들을 지속적으로 업데이트해 드릴 수 있도록 노력하겠습니다.강의를 수강해 주셔서 진심으로 감사드리며, 학습 중 다른 궁금한 점이 생기면 언제든 질문 남겨주세요!감사합니다.우리동네코딩 스튜디오 드림
- 0
- 2
- 34
Q&A
[화면 이슈]useReduce 첫걸음 - 행동 중심 상태 변화를 처음 경험하기 화면
안녕하세요, eddie님! 우리동네코딩 스튜디오의 지식공유자입니다.우선 강의 시청 중에 불편을 드려 정말 죄송하다는 말씀부터 올립니다. 제보해 주신 [useReducer 첫걸음] 강의의 특정 구간(00:50 - 01:08) 블랙아웃 현상을 확인하였고, 방금 수정된 영상으로 즉시 교체하여 정상적으로 반영해 두었습니다.꼼꼼하게 확인하고 업로드했어야 하는데, 소중한 시간을 내어 수강하시던 중에 흐름을 끊어 드린 것 같아 마음이 무겁습니다. 이렇게 직접 스크린샷까지 첨부해 오류를 짚어주셔서 정말 큰 도움이 되었고, 진심으로 감사드립니다.🎁 감사의 마음을 담은 작은 선물죄송하고 감사한 마음을 담아, eddie님께 작은 보답을 해드리고 싶습니다.현재 우리동네코딩 스튜디오에서 운영 중인 강의들 중, 평소 수강을 희망하셨던 강의나 관심 있는 주제가 있으시다면 아래 메일로 편하게 연락해 주세요. 확인하는 대로 바로 수강하실 수 있도록 전달해 드리겠습니다.이메일:jeony0535@naver.com앞으로는 더 꼼꼼한 검수를 통해 학습에만 집중하실 수 있는 최고의 환경을 만들도록 노력하겠습니다. 다시 한번 제보해 주셔서 감사합니다!우리동네코딩 스튜디오 드림
- 0
- 2
- 9
Q&A
next.js 를 2대이상 실행하는 경우 제공하는 cache 기능들은 어떻게 되나요?
안녕하세요 icoon22님! 질문의 깊이를 보니 단순히 프레임워크의 기능적인 사용법을 넘어 운영체제의 메모리와 네트워크가 얽히는 분산 시스템의 본질까지 아주 깊게 고민하고 계신 것이 느껴지며 클론 코딩 수준을 벗어나 실제 프로덕션 레벨의 아키텍처를 설계하려는 모습이 정말 훌륭합니다. Next.js를 마이크로서비스 아키텍처 환경에서 프론트엔드를 위한 백엔드 즉 BFF로 활용하며 트래픽을 감당하기 위해 클라우드 환경인 AWS ECS나 Kubernetes 등에서 서버를 두세 대 이상으로 스케일 아웃하여 늘리게 되면 가장 먼저 캐시의 파편화라는 문제에 직면하게 됩니다. 기본적으로 Next.js의 내장 데이터 캐시는 해당 서버가 떠 있는 파드나 인스턴스의 로컬 파일 시스템 및 메모리에 저장되는 방식으로 동작하기 때문입니다. 예를 들어 대규모 이커머스 서비스의 특가 이벤트 상황을 실무 시나리오로 가정해 보겠습니다. 사용자가 특정 상품의 상세 페이지를 요청하면 로드밸런서가 이 요청을 1번 서버로 보내고 1번 서버는 백엔드에서 데이터를 가져와 자신의 로컬에 캐싱합니다. 그런데 1분 뒤 다른 사용자가 똑같은 상품을 요청했을 때 라운드로빈 방식 등에 의해 2번 서버로 요청이 가면 2번 서버의 로컬에는 해당 캐시가 없으므로 또다시 백엔드 API를 찔러 데이터를 가져와야 합니다. 결과적으로 BFF를 도입하여 백엔드 부하를 줄이려 했음에도 인스턴스 개수만큼 백엔드 호출이 중복 발생하고 심지어 서버마다 갱신 주기가 어긋나 데이터 불일치까지 발생합니다.현재 프로덕션 환경에서는 이 문제를 해결하기 위해 커스텀 캐시 핸들러를 설정하여 로컬 캐시가 아닌 Redis 같은 중앙 집중형 메모리 데이터베이스를 바라보도록 아키텍처를 변경해야 합니다. 이를 구현하기 위해 아래와 같이 캐시 핸들러 파일을 작성하게 됩니다.// cache-handler.mjs (2026년 Next.js 분산 Redis 캐시 핸들러 아키텍처 예시) import { CacheHandler } from '@neshca/cache-handler'; import createLruCache from '@neshca/cache-handler/local-lru'; import createRedisCache from '@neshca/cache-handler/redis-strings'; import { createClient } from 'redis'; CacheHandler.onCreation(async () => { let client; try { // 다중 인스턴스가 공통으로 바라보는 Redis 클러스터에 연결합니다. client = createClient({ url: process.env.REDIS_URL }); await client.connect(); } catch (error) { console.warn('Redis 연결 실패, 로컬 캐시로 안전하게 폴백합니다.', error); } return { handlers: [ client ? createRedisCache({ client }) : null, createLruCache(), // 단일 진실 공급원인 Redis에 장애가 생겼을 때를 대비한 방어 로직입니다. ], }; }); export default CacheHandler; 이렇게 구성하고 설정 파일에 핸들러를 등록하면 1번 서버가 Redis에 캐시를 구워두었을 때 2번 서버와 3번 서버도 Redis를 조회하여 단일 진실 공급원으로서의 캐시를 온전히 공유할 수 있게 됩니다. 이러한 캐시 아키텍처의 변화는 과거 단순한 데이터 전달 통로였던 BFF를 초고속 캐시 레이어로 진화시킨 캐싱 혁명과 맞닿아 있습니다. Next.js 16 환경에서는 React 19의 서버 컴포넌트와 결합된 데이터 캐시가 변경이 잦지 않은 백엔드의 응답을 BFF 단에서 완전히 정적 상태로 얼려버리며 revalidateTag 함수를 통해 수십 개의 마이크로서비스 중 데이터가 변경된 특정 서비스의 캐시만 무효화하는 세밀한 제어가 가능합니다.그런데 서버에서 이렇게 적극적으로 캐싱을 하는 상황에서 클라이언트에도 TanStack Query라는 강력한 캐시 도구가 존재한다면 어느 캐시가 최신 데이터인가를 두고 충돌이 발생할 수밖에 없으므로 이 둘의 역할을 명확히 분리하고 동기화하는 것이 핵심입니다. 사용자가 좋아요를 누르거나 리뷰를 수정하는 등의 상태 변경 액션을 취할 때 서버의 캐시와 클라이언트의 캐시를 순차적으로 날려주는 파이프라인을 구축해야 이전 데이터가 다시 나타나는 고스트 데이터 버그를 막을 수 있습니다. 2026년 실무에서는 Next.js의 서버 액션과 TanStack Query의 v5 이상을 결합하여 이 동기화를 아래와 같이 완벽하게 처리합니다.// 1. Server Action (actions/review.ts) 'use server'; import { revalidateTag } from 'next/cache'; export async function updateReviewAction(reviewId: string, content: string) { // 마이크로서비스 백엔드의 리뷰 수정 API를 호출하여 실제 데이터를 변경합니다. const res = await fetch(`http://api.internal/reviews/${reviewId}`, { method: 'PUT', body: JSON.stringify({ content }), }); if (!res.ok) throw new Error('리뷰 수정 실패'); // Redis에 저장된 해당 분산 캐시를 즉시 무효화하여 모든 BFF 인스턴스가 최신 상태를 알게 합니다. revalidateTag(`review-${reviewId}`); return { success: true }; } // 2. Client Component (components/ReviewEditor.tsx) 'use client'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { updateReviewAction } from '@/actions/review'; export default function ReviewEditor({ reviewId }: { reviewId: string }) { const queryClient = useQueryClient(); const mutation = useMutation({ mutationFn: (content: string) => updateReviewAction(reviewId, content), onSuccess: () => { // 서버 측 Redis 캐시가 날아간 성공 응답을 받은 직후, 클라이언트의 캐시도 날려줍니다. // 이를 통해 즉시 가장 최신의 서버 상태를 다시 Fetching 하여 완벽한 동기화를 이룹니다. queryClient.invalidateQueries({ queryKey: ['review', reviewId] }); } }); return ( // 사용자 UI 및 이벤트 핸들러 바인딩... mutation.mutate('업데이트된 리뷰 내용')}>저장 ); } 이 코드를 통해 서버 액션에서 서버 캐시를 먼저 무효화하고 성공 응답 후 클라이언트에서 TanStack Query의 캐시를 무효화하여 순차적으로 가장 최신화된 데이터를 다시 받아오게 만드는 것이 완벽한 동기화 파이프라인입니다. 추가적으로 다중 인스턴스 BFF 환경을 실제 클라우드에 구축하실 때 발생할 수 있는 장애 상황들을 고려하여 캐시 스탬피드 혹은 썬더링 허드라고 불리는 현상을 방지하는 것을 반드시 짚고 넘어가셔야 합니다. 무거운 통계 데이터 캐시가 만료된 찰나의 순간에 대규모 트래픽이 몰리면 3대의 BFF 인스턴스가 동시에 캐시가 없음을 인지하고 백엔드로 쿼리를 무수히 날려 데이터베이스가 뻗어버릴 수 있으므로 백그라운드에서 캐시를 갱신하는 패턴을 활용하고 Redis 분산 락 기술을 도입하여 여러 인스턴스 중 단 하나의 인스턴스만이 갱신 작업을 수행하도록 제어해야 합니다. 또한 세션과 인증 상태의 무상태성을 철저히 유지하여 유저가 1번 서버에서 로그인한 후 로드밸런서에 의해 2번 서버로 접속했을 때도 로그인 상태가 유지되도록 JWT 기반의 무상태 인증을 구현하거나 세션 스토어 자체를 별도의 Redis 클러스터로 분리하여 관리해야 세션 단절 이슈를 막을 수 있습니다. 마지막으로 수많은 마이크로서비스가 얽힌 환경에서는 유저가 에러를 겪었을 때 어느 구간에서 터진 것인지 단순 로그로는 추적이 불가능하므로 클라이언트 최초 요청 시 고유한 트레이스 ID를 헤더에 붙여서 보내고 BFF가 이를 로그에 남김과 동시에 백엔드 API로 전달하는 분산 트레이싱 릴레이 시스템을 구축해야만 모니터링 도구에서 전체 요청의 흐름과 병목 구간을 시각적으로 한눈에 파악할 수 있게 됩니다.이러한 아키텍처적인 고민을 더욱 깊이 있게 발전시키기 위해 앞으로 몇 가지 컴퓨터 공학의 근본적인 주제들을 추가로 공부해보시는 것도 큰 도움이 될 것 같습니다. 웹 프레임워크의 화려한 기능 이면에 있는 운영체제 수준의 스레드 관리나 네트워크 프로토콜의 본질을 파고들면 시야가 한층 넓어집니다. 예를 들어 마이크로서비스 간의 통신에서 REST를 넘어선 gRPC의 원리나, 분산 환경에서의 데이터베이스 트랜잭션 동기화 모델을 학습하시면 지금 직면한 문제들을 훨씬 더 구조적으로 해결하실 수 있습니다. 나아가 최근 급부상하는 인공지능과 대규모 언어 모델을 이러한 인프라에 어떻게 접목할지 고민해보시는 것도 추천해 드립니다. 가령 파인튜닝된 경량 모델을 활용해 BFF 레이어에서 들어오는 트래픽의 의도를 지능적으로 라우팅하거나, 모델 예측을 통해 캐시 만료 시점을 동적으로 조절하는 등 차세대 엔지니어링의 아주 흥미로운 가능성을 탐구해보실 수 있을 것입니다.icoon22님 이처럼 최신 프레임워크의 편리한 마법 뒤에는 탄탄한 컴퓨터 공학적 기초가 자리 잡고 있습니다. 지금처럼 근본적인 동작 원리와 아키텍처의 한계를 묻고 탐구하는 학습 태도를 유지하신다면 어떤 복잡한 대규모 환경에서도 흔들리지 않는 견고한 프로덕션 시스템을 설계하실 수 있을 것입니다. 파이팅입니다!
- 0
- 2
- 40
Q&A
17강 zustand store 서버에서 생성
안녕하세요 byeong님! 많은 분들이 "어차피 서버에서 가져온 데이터라면 TanStack Query만 사용하면 되지, 왜 굳이 Zustand에 초기값으로 주입하는 번거로운 과정을 거쳐야 할까?"라는 의문을 가지십니다. 핵심부터 말씀드리자면, 서버 데이터를 '단순히 보여주기만' 할 때는 TanStack Query가 정답입니다. 하지만 서버 데이터를 초기 '씨앗(Seed)'으로 삼아 클라이언트에서 복잡한 상호작용(예: 폼 입력, 임시 상태 저장 등)을 처리해야 할 때는, 이 데이터를 Zustand로 넘겨받아 관리하는 것이 훨씬 유리하고 안전하기 때문입니다. 상태의 성격이 '서버의 스냅샷'에서 '사용자의 상호작용'으로 넘어가는 경계선인 셈이죠.이를 실제 코드로 구현할 때 가장 먼저 주의해야 할 점은 Next.js App Router 환경에서의 Zustand 스토어 스코프(Scope)입니다. React 렌더링이 일어나는 서버 환경(Node.js)은 여러 요청이 동일한 서버 인스턴스를 공유합니다. 따라서 일반적인 싱글 페이지 애플리케이션(SPA)처럼 컴포넌트 외부에 전역 스토어 변수를 선언하게 되면, 'A 유저의 초기값이 B 유저의 화면에 노출되는' 치명적인 보안 버그가 발생합니다.이를 방지하려면 반드시 Context API를 활용하여 렌더링 사이클(혹은 사용자 요청)마다 독립적인 스토어 인스턴스를 새로 생성해야 합니다. 아래와 같이 바닐라 Zustand 스토어를 생성하는 팩토리 함수와, 이를 하위 컴포넌트에 공급하는 Provider 패턴을 작성하는 것이 그 표준적인 해결책입니다.import { createStore } from 'zustand'; import { createContext, useRef, useContext, ReactNode } from 'react'; import { useStore } from 'zustand'; // 1. 상태 타입 및 스토어 생성 팩토리 함수 정의 (전역 스토어가 아님에 주의합니다) interface ProfileState { name: string; setName: (name: string) => void; } export const createProfileStore = (initialName: string) => { return createStore()((set) => ({ name: initialName, setName: (name) => set({ name }), })); }; export type ProfileStore = ReturnType; // 2. React Context 및 Provider 생성 export const ProfileContext = createContext(null); export function ProfileProvider({ children, initialName }: { children: ReactNode, initialName: string }) { const storeRef = useRef(null); if (!storeRef.current) { // 최초 렌더링 시에만 서버로부터 받은 초기값을 주입하여 스토어 인스턴스를 생성합니다. storeRef.current = createProfileStore(initialName); } return ( {children} ); } // 3. 컴포넌트에서 스토어를 쉽게 사용하기 위한 커스텀 훅 export function useProfileContext(selector: (state: ProfileState) => T): T { const store = useContext(ProfileContext); if (!store) throw new Error('ProfileProvider가 필요합니다.'); return useStore(store, selector); } 이렇게 안전한 스토어 공급망을 구축했다면, 이제 복잡한 다중 스텝 폼 시나리오를 서버 컴포넌트에서 안전하게 시작할 수 있습니다. 사용자가 프로필 수정 페이지에 진입하면, 서버 컴포넌트는 데이터베이스나 외부 API를 통해 유저의 기존 프로필 정보를 페치(Fetch)합니다. 그리고 이 데이터를 앞서 만든 Provider의 initialName으로 밀어 넣어, 클라이언트 상태의 초기값으로 설정하는 역할을 수행합니다.import { ProfileProvider } from '@/components/ProfileProvider'; import ProfileForm from '@/components/ProfileForm'; export default async function ProfilePage() { // 서버에서 초기 데이터를 Fetching 합니다. // 이 단계에서는 TanStack Query의 prefetchQuery와 Hydration Boundary를 조합하여 캐시를 채울 수도 있습니다. const response = await fetch('https://api.example.com/user/profile'); const initialData = await response.json(); return ( // 서버에서 가져온 초기값을 Provider에 주입하여 클라이언트 상태의 '씨앗'으로 삼습니다. ); } 마지막으로 클라이언트, 즉 Zustand의 역할은 사용자가 입력칸을 채우고 지우는 모든 과정을 API 통신 없이 철저히 브라우저 메모리 내에서만 가볍고 빠르게 관리하는 것입니다.사용자가 타이핑을 할 때마다 TanStack Query의 캐시를 억지로 업데이트하는 것은 '서버 상태 동기화'라는 본래 목적과 맞지 않으며, 불필요한 성능 저하를 유발합니다. 따라서 아래 코드처럼 Zustand를 통해 즉각적인 UI 반응성을 확보하고, 모든 수정이 끝난 후 사용자가 '최종 저장' 버튼을 누를 때 TanStack Query의 Mutation을 통해 한 번에 서버로 전송하는 것이 바람직합니다.'use client'; import { useProfileContext } from '@/components/ProfileProvider'; import { useMutation } from '@tanstack/react-query'; export default function ProfileForm() { // 서버와의 동기화는 끊어지고, 오직 클라이언트 메모리에서만 상태를 조작합니다. const name = useProfileContext(state => state.name); const setName = useProfileContext(state => state.setName); // 사용자의 상호작용이 모두 끝난 후 최종 결과물만 서버에 동기화하기 위한 Mutation입니다. const updateProfileMutation = useMutation({ mutationFn: (newName: string) => fetch('/api/user/profile', { method: 'POST', body: JSON.stringify({ name: newName }) }) }); return ( { e.preventDefault(); updateProfileMutation.mutate(name); // 폼이 제출될 때 비로소 서버와 다시 소통합니다. }}> setName(e.target.value)} placeholder="이름을 입력하세요" /> 최종 저장 ); } 이처럼 상품 필터링의 가격 슬라이더나 이미지 크롭 툴의 캔버스 상태처럼, 초당 수십 번씩 바뀌는 순수 UI 상태는 Zustand가 가볍고 빠르게 처리하도록 맡겨야 합니다. 현업에서 상태 관리를 설계할 때 "이 상태의 주인이 누구인가?"를 스스로에게 질문해 보세요. 내 화면에서 내가 조작하기 위한 임시 상태라면, 초기 데이터만 서버에서 받고 클라이언트로 그 통제권을 완전히 분리하는 것이 맞습니다.또한, 단일 진실 공급원(Single Source of Truth)을 훼손하지 않기 위해 Zustand에 서버 상태를 계속 복사하여 병렬로 유지하려는 안티 패턴(Anti-pattern)을 피해야 합니다. 초기 렌더링 시점에 딱 한 번만 주입한 후, 해당 데이터의 생사결정권을 완전히 Zustand가 가지도록 독립시키는 것이 핵심입니다.byeong님, 이 답변에 포함된 아키텍처 패턴이 실무의 그림을 명확히 그리시는 데 도움이 되셨기를 바랍니다. 상태 관리의 경계를 명확히 구분하는 이 감각은 앞으로 복잡한 웹 애플리케이션을 견고하게 설계하실 때 아주 강력한 무기가 될 것입니다!참고해주세요!
- 1
- 1
- 11
Q&A
문의관련 문의
안녕하세요, 보헤미안님.먼저 강의 이용에 불편을 드려 대단히 죄송합니다.문의해주신 80강의 테스트 파트는 수강생분들의 개별 실행 환경이나 소프트웨어 버전에 따라 오류가 빈번하게 발생하는 지점이었습니다. 이로 인해 학습의 흐름이 끊기는 문제를 방지하고자 부득이하게 실습 파트를 제외하는 결정을 내리게 되었습니다.저 또한 해당 부분이 즉시 수정 및 업데이트된 것으로 파악하고 있었으나, 시스템상 반영이 누락되었거나 지연된 것으로 보입니다. 다시 한번 꼼꼼하게 챙기지 못해 혼선을 드린 점 사과드립니다.소중한 시간 내어 다시 한번 문제를 짚어주셔서 감사드리며, 지적해 주신 부분은 지금 즉시 확인하여 중복 내용을 제거하고 올바른 내용으로 수정 조치하겠습니다.원활한 학습을 위해 최선을 다하겠습니다. 감사합니다.
- 0
- 2
- 32
Q&A
렌더링 차단 리소스 javascript 실행에 관련해서 질문 있습니다.
안녕하세요 eddie님! 질문 주셔서 감사합니다. 질문해주신 바디 태그 최하단에 스크립트가 위치하고 그 내부에서 DOM의 너비, 높이, 배경색을 변경할 때 어떤 일이 발생하는지에 대해 상세한 예시 코드와 함께 확실하게 짚어드리겠습니다.우선 결론부터 말씀드리자면, eddie님이 이 수업을 들으시기 전에 생각하셨던 것처럼 리플로우와 리페인트 과정이 발생하는 것이 맞습니다. 브라우저의 파서는 HTML 문서를 위에서 아래로 순차적으로 읽어 내려가며 DOM 트리를 구축합니다.파서가 바디 태그 가장 끝에 도달하여 스크립트 태그를 만나면, eddie님이 이해하신 대로 남은 HTML 문서의 파싱을 잠시 멈추고 자바스크립트를 다운로드 및 실행하게 됩니다. 하지만 여기서 중요한 포인트는 스크립트가 실행되는 그 시점에는 이미 스크립트 태그 위에 작성된 HTML 요소들이 파싱되어 DOM 트리에 추가되어 있다는 사실입니다.브라우저는 초기 DOM과 CSSOM을 결합해 렌더 트리를 만들고 화면에 그릴 준비를 하는데, 자바스크립트가 개입하여 이미 만들어진 DOM 노드의 스타일을 동적으로 변경하게 되면 브라우저는 렌더링 파이프라인의 특정 단계를 다시 실행해야만 합니다. 구체적인 동작 원리를 아래의 예시 코드를 보며 설명해 드리겠습니다. 렌더링 테스트 #box { width: 100px; height: 100px; background-color: blue; } 테스트 박스 // 파서가 여기까지 도달하면 파싱을 멈추고 아래 코드를 실행합니다. const boxElement = document.getElementById('box'); // DOM에 접근하여 요소의 기하학적 속성(width, height)을 변경합니다. boxElement.style.width = '200px'; boxElement.style.height = '200px'; // 요소의 시각적 속성(background)을 변경합니다. boxElement.style.backgroundColor = 'red'; 위 코드를 살펴보시면, 브라우저가 위에서부터 문서를 읽어 내려오며 아이디가 box인 div 요소를 먼저 DOM 트리에 등록합니다. 그리고 문서 끝에서 자바스크립트가 실행되면서 해당 요소의 속성을 동적으로 변경하게 됩니다. 이때 너비와 높이 값은 요소의 기하학적 크기와 위치를 결정하는 속성입니다.자바스크립트가 이 값을 변경하는 순간, 브라우저는 이 요소뿐만 아니라 영향을 받는 주변 요소들의 위치와 크기를 다시 계산해야 하며, 이 과정이 바로 레이아웃 단계의 재실행, 즉 리플로우입니다. 크기와 위치 계산이 끝나면 화면에 새로운 색상인 빨간색을 입혀야 하는데, 배경색과 같이 요소의 기하학적 구조에는 영향을 주지 않고 시각적인 형태만 바꾸는 속성이 변경되었으므로 페인트 단계가 다시 실행되는 리페인트가 연이어 발생하게 됩니다.즉, 스크립트가 HTML 파싱을 잠시 멈추는 것은 맞지만, 이미 생성된 DOM 객체를 자바스크립트로 조작하는 행위 자체는 렌더링 엔진에게 렌더 트리를 업데이트하고 화면을 다시 그리라는 명령을 내리는 것과 같기 때문에 리플로우와 리페인트를 피할 수 없는 것입니다.이해를 위해 다시 한 번 렌더링 파이프라인의 핵심 용어 하나하나를 순서대로 짚어드리자면 다음과 같이 설명할 수 있습니다. 먼저 파싱은 브라우저가 HTML이나 CSS 코드를 읽고 이해할 수 있는 구조로 변환하는 과정을 말합니다. 이 과정을 통해 HTML 문서의 각 요소들을 트리 형태로 구성하여 자바스크립트가 접근하고 조작할 수 있도록 만든 문서 객체 모델이 바로 DOM이며, CSS 스타일 정보들을 모아 트리 형태로 구성한 것이 CSSOM입니다.브라우저는 이 DOM과 CSSOM을 결합하여 화면에 실제로 그려질 요소들만 모아 렌더 트리라는 최종 트리를 구축하게 됩니다. 렌더 트리가 완성되면 이를 기반으로 각 요소가 화면의 어느 위치에 어느 정도의 크기로 배치될지 기하학적인 형태를 계산하게 되는데, 이를 레이아웃 또는 리플로우라고 부릅니다.크기와 위치 계산이 끝난 후에는 실제 픽셀을 채워 넣어 색상이나 이미지 같은 시각적인 부분들을 화면에 그리게 되며, 이 단계를 페인트 또는 리페인트라고 합니다. 마지막으로 페인트 단계에서 그려진 여러 레이어들을 하나로 합성하여 최종적으로 화면에 표시하는 단계를 컴포지팅이라고 합니다.이러한 렌더링 과정을 직접 눈으로 확인하고 실습해 보시려면, 크롬 브라우저의 개발자 도구를 열고 성능 탭을 적극적으로 활용해 보시는 것을 추천해 드립니다. 성능 탭에서 새로고침 버튼 모양인 기록 아이콘을 누르고 페이지가 로드되는 과정을 캡처해 보시면, 하단 이벤트 로그나 메인 스레드 작업 내역에서 스타일 재계산, 리플로우, 리페인트 이벤트가 자바스크립트 실행 직후에 연달아 발생하는 것을 명확하게 확인하실 수 있습니다. 자바스크립트 코드를 지웠을 때와 추가했을 때의 성능 기록을 비교하면서 추적해 보시면 자바스크립트 코드가 렌더링 파이프라인에 미치는 영향을 훨씬 더 깊고 확실하게 체득하실 수 있을 것입니다.여기에 덧붙여, 현업 실무에서 렌더링 성능을 끌어올리기 위해 이러한 리플로우와 리페인트를 최소화하는 몇 가지 유용한 최적화 노하우를 가이드로 나누어 드리겠습니다.첫 번째로 돔 조작은 한 번에 모아서 처리하는 것이 좋습니다. 자바스크립트로 요소의 스타일을 여러 번 나누어 변경하면 그 횟수만큼 리플로우가 발생할 수 있으므로, 가급적 변경할 스타일들을 CSS 클래스로 미리 정의해두고 클래스명 하나를 조작하여 스타일을 한 번에 일괄 변경하는 것이 성능에 훨씬 유리합니다.두 번째로 애니메이션을 구현할 때는 트랜스폼과 오퍼시티 속성을 활용하는 것이 바람직합니다. 요소의 크기나 위치를 직접 조작하면 매 프레임마다 리플로우가 발생하여 화면이 뚝뚝 끊기는 현상이 발생할 수 있지만, 해당 속성들을 사용하면 리플로우와 리페인트를 건너뛰고 바로 합성 단계만 거치기 때문에 그래픽 카드의 도움을 받아 훨씬 부드러운 렌더링이 가능해집니다.세 번째로 보이지 않는 상태에서 돔을 다루는 방법이 있습니다. 특정 컨테이너 안에 아주 많은 돔 조작이 필요하다면 먼저 해당 요소를 화면에서 숨겨 리플로우를 한 번만 발생시킨 후, 그 상태에서 필요한 모든 조작을 마치고 다시 화면에 보이게 하여 중간 과정에서의 불필요한 렌더링을 막을 수 있습니다. 또한 가상의 돔 공간인 도큐먼트 프래그먼트를 활용하여 메모리상에서 노드를 모두 완성한 후 실제 돔에 단 한 번만 추가하는 방법도 실무에서 매우 자주 쓰이는 패턴이니 꼭 참고해 보시기 바랍니다.이렇게 좋은 질문 주셔서 감사합니다. eddie님처럼 원리를 파고드는 열정적인 수강생분들이 남겨주시는 예리한 질문들은 제가 앞으로 더 깊이 있는 강의를 제작하고 커리큘럼을 발전시키는 데 정말 큰 도움이 됩니다. 감사의 마음을 담아, 저희 우리 동네 코딩 스튜디오에서 제공하는 강의 중 추가로 필요하거나 수강하고 싶으신 강의가 있다면jeony0535@naver.com으로 언제든지 편하게 연락해 주시기 바랍니다. 확인 후 원하시는 강의의 수강 쿠폰을 기쁜 마음으로 전달해 드리겠습니다. 앞으로도 학습하시면서 궁금한 점이 생기시면 언제든 편하게 질문 남겨주세요. eddie님의 프론트엔드 학습을 항상 진심으로 응원합니다!감사합니다!
- 0
- 2
- 39
Q&A
렌더링 차단 리소스 관련해서 CSS 질문이 있습니다.
안녕하세요 eddie님! 수업 내용 중 프론트엔드 성능 최적화의 핵심인 렌더링 차단 리소스에 대해 깊이 고민하시고 질문을 남겨주셔서 정말 반갑습니다. 막연했던 개념을 확실히 잡아가고 계신 것 같아 훌륭합니다. 브라우저가 화면을 그리는 원리와 함께 질문해주신 3가지 최적화 기법인 media 속성, loadCSS, 동적 분리에 대해 상세한 예시 코드와 함께 하나씩 확실하게 짚어드리겠습니다.먼저 CSS가 왜 렌더링을 차단하는지 그 원리를 이해하는 것이 중요합니다. 브라우저는 HTML을 읽다가 외부 스타일시트를 불러오는 link 태그를 만나면, 이 CSS를 모두 다운로드하고 해석하여 트리를 구성하기 전까지는 화면에 아무것도 그리지 말아야겠다고 판단합니다. 이는 스타일이 적용되지 않은 날것의 HTML이 잠깐 보였다가 스타일이 입혀지는 현상을 막기 위해서입니다. 하지만 당장 첫 화면에 필요 없는 CSS까지 다운로드하느라 빈 하얀 화면만 오래 보여주는 것은 성능에 치명적이므로, 이를 해결하기 위해 질문해주신 기법들을 사용하게 됩니다.첫 번째로 media 속성을 통한 렌더링 차단 방지 기법에 대해 설명해 드리겠습니다. eddie님이 질문하신 media 속성이 CSS 파일 내부에서 쓰는 media query를 말씀하신 것이 맞는지에 대한 대답은, 개념은 같지만 적용 위치가 다르다는 것입니다. 파일 내부의 구문이 아니라 HTML의 link 태그 자체에 media 속성을 부여하는 것을 의미합니다. 브라우저는 이 속성값을 보고 해당 CSS가 지금 당장 렌더링하는 데 필요한지를 판단하게 되는데, 아래의 실무적인 예시 코드를 보며 설명해 드리겠습니다. 코드에서 보시는 것처럼, link 태그에 단순히 href로 모든 스타일을 연결해두면 무조건 다운로드가 완료될 때까지 렌더링이 멈추는 차단 현상이 발생합니다. 하지만 파일 경로 뒤에 media 속성으로 특정 조건을 달아주면, 조건에 맞지 않는 CSS는 다운로드는 하되 렌더링을 가로막지 않게 됩니다. 즉, 파일 자체를 기기나 상황에 맞게 쪼개어 최적화하는 원리입니다.두 번째로 말씀하신 loadCSS와 관련된 비동기 CSS 로딩 기법입니다. loadCSS는 원래 특정 그룹에서 만든 자바스크립트 기반의 비동기 CSS 로더 라이브러리의 이름으로, 과거에는 CSS가 렌더링을 차단하는 것을 막기 위해 실무에서 많이 사용했습니다. 하지만 현재 모던 웹 환경에서는 이 라이브러리를 직접 가져다 쓰기보다는 HTML5 표준인 preload 속성을 활용한 패턴으로 진화했으며, 이 패턴 자체를 관용적으로 loadCSS 기법이라고 부르기도 합니다. 이 기법은 푸터 영역의 스타일이나 나중에 클릭 시 열릴 모달 창 스타일처럼 당장 첫 화면에 필요 없는 비핵심 CSS를 화면 렌더링과 동시에 뒷단에서 몰래 다운로드하는 최적화 방식입니다. 모던 loadCSS 패턴의 적용 예시 코드를 살펴보겠습니다. 코드를 보시면, 처음에는 preload 설정 덕분에 렌더링을 차단하지 않고 파일만 빠르게 다운로드하며, 완료되는 순간 자바스크립트를 이용해 실제 스타일을 화면에 적용하게 됩니다. 여기에 추가로, 사용자의 브라우저 환경에서 자바스크립트가 꺼져있을 경우를 대비한 안전장치로서 noscript 태그 안에 기존 방식의 일반적인 link 태그를 하나 더 넣어주는 방식으로 완벽하게 구현할 수 있습니다.세 번째로 언급된 동적으로 분리하는 방식은 처음부터 HTML 문서에 link 태그를 모두 적어두는 것이 아니라, 자바스크립트를 이용해 특정 상황이 되었을 때만 필요한 CSS를 문서에 주입하는 방식입니다. React나 Next.js 같은 모던 프레임워크 환경에서 개발을 하시다 보면 번들러가 이 과정을 알아서 처리해 주는 것을 보실 수 있는데, 이를 보통 코드 스플리팅이라고 칭합니다. 바닐라 자바스크립트를 활용하여 구현하는 구체적인 예시 코드를 통해 흐름을 설명해 드리겠습니다.// 결제하기 버튼 요소를 찾습니다. const paymentButton = document.querySelector('#pay-btn'); // 버튼에 클릭 이벤트 리스너를 달아줍니다. paymentButton.addEventListener('click', () => { // 1. 버튼을 누르는 순간 동적으로 link 태그 요소를 생성합니다. const link = document.createElement('link'); link.rel = 'stylesheet'; link.href = '/css/payment-modal.css'; // 결제창 전용 CSS 경로 // 2. 문서의 head 태그 안에 주입하여 그제야 브라우저가 다운로드하고 렌더링에 반영하게 합니다. document.head.appendChild(link); // 3. 이후 결제 모달창을 띄우는 로직을 실행합니다. openPaymentModal(); });이 코드처럼 사용자가 결제하기 버튼을 눌렀을 때만 결제 모달창의 CSS를 로드하도록 구현할 수 있습니다. 사용자가 버튼을 누르는 순간 내부 로직에서 동적으로 새로운 link 태그 요소를 생성하고 문서에 주입하는 것입니다. 이렇게 구현하면 초기 로딩 시 무조건 다운로드해야 할 CSS 파일의 전체 크기가 확연히 줄어들기 때문에, 첫 화면이 뜨는 속도가 비약적으로 상승하는 이점을 얻을 수 있습니다.요약하자면, 가장 첫 화면을 그리는 데 필수적인 핵심 CSS 요소들만 태그 내부에 인라인으로 삽입하거나 기본 배치로 두고, 그 외의 나머지 스타일들은 기기별로 쪼개어 media 속성을 활용하거나, preload를 이용해 뒷단에서 비동기로 몰래 받아오거나, 특정 컴포넌트가 화면에 나타날 때 자바스크립트를 통해 동적으로 분리하여 가져오도록 설계하는 것이 프론트엔드 렌더링 최적화의 핵심 전략입니다.이러한 개념들을 직접 실습해 보시려면, 현재 작업 중이신 프로젝트나 간단한 HTML 파일을 열고 브라우저의 개발자 도구(F12)에 있는 '네트워크(Network)' 탭을 적극적으로 활용해 보시는 것을 추천해 드립니다. 처음에는 모든 CSS를 기본 link 태그로 불러올 때의 로딩 속도와 자원 다운로드 순서를 확인해 보시고, 그다음에는 오늘 설명해 드린 media 속성이나 preload, 그리고 자바스크립트를 활용한 동적 분리 기법을 코드에 하나씩 적용해 보시기 바랍니다. 네트워크 탭에서 불필요한 스타일 자원이 언제 다운로드되는지, 그리고 화면이 그려지는 시점이 어떻게 앞당겨지는지 눈으로 직접 비교해 보시면 훨씬 더 깊고 확실하게 체득하실 수 있을 것입니다.이렇게 깊이 고민하시고 좋은 질문을 남겨주셔서 정말 감사합니다. eddie님처럼 열정적인 수강생분들이 남겨주시는 예리한 질문들은 제가 앞으로 더 좋은 강의를 제작하고 커리큘럼을 발전시키는 데 정말 큰 도움이 됩니다. 감사의 마음을 담아, 저희 우리 동네 코딩 스튜디오에서 제공하는 강의 중 추가로 필요하거나 수강하고 싶으신 강의가 있다면 jeony0535@naver.com 으로 언제든지 편하게 연락해 주시기 바랍니다. 확인 후 원하시는 강의의 수강 쿠폰을 기쁜 마음으로 전달해 드리겠습니다. 앞으로도 학습하시면서 궁금한 점이 생기시면 언제든 질문 남겨주세요. eddie님의 프론트엔드 학습을 항상 응원합니다!
- 0
- 2
- 29
Q&A
22강 강의 영상 문의 드립니다.
안녕하세요, dgh12님. 강의 수강 중 계속해서 불편을 드려 정말 송구한 마음뿐입니다.문의하신 '22강. [CS 아키텍처] 매크로 캐싱(Page)의 한계와 마이크로 캐싱(Component) 기술' 영상이 현재 노출되지 않는 점 확인하였습니다.제보해 주신 직후 바로 원인을 파악 중이며, 지금 바로 플랫폼에 다시 반영하여 정상적으로 시청하실 수 있도록 조치하겠습니다. 학습 흐름이 끊기게 해드려 강사로서 마음이 무겁습니다.dgh12님께서 남겨주시는 피드백 하나하나가 강의의 완성도를 높이는 데 큰 힘이 되고 있습니다. 앞서 말씀드린 nhcodingstudio 강의 관련 할인 쿠폰 혜택은 언제든 jeony0535@naver.com으로 메일 주시면 바로 처리해 드릴 테니 부담 없이 말씀해 주세요.조치가 완료되는 대로 다시 한번 확인 부탁드리며, 다시 한번 진심으로 사과드립니다.감사합니다.
- 0
- 2
- 45
Q&A
아래 위치에 동영상이 보이지 않습니다.
안녕하세요, 심재화님. 강의 수강 중 계속해서 불편을 드려 정말 송구한 마음뿐입니다.문의하신 '18강. [Next.js 실전] redirect() 함수로 구현하는 완벽한 동선 제어' 영상이 현재 노출되지 않는 점 확인하였습니다.제보해 주신 직후 바로 원인을 파악 중이며, 지금으로부터 5분 내로 플랫폼에 다시 반영하여 정상적으로 시청하실 수 있도록 조치하겠습니다. 학습 흐름이 자주 끊기게 해드려 강사로서 마음이 무겁습니다.심재화님께서 남겨주시는 피드백 하나하나가 강의의 완성도를 높이는 데 큰 힘이 되고 있습니다. 앞서 말씀드린 nhcodingstudio 강의 관련 할인 쿠폰 혜택은 언제든 jeony0535@naver.com으로 메일 주시면 바로 처리해 드릴 테니 부담 없이 말씀해 주세요.조치가 완료되는 대로 다시 한번 확인 부탁드리며, 다시 한번 진심으로 사과드립니다.감사합니다.
- 0
- 1
- 49




