<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>BEPULJANG Blog</title><description>Vibe coding, AI dev tools, and stories from people who build.</description><link>https://blog.bepuljang.co.kr/</link><language>en-US</language><atom:link href="https://blog.bepuljang.co.kr/en/rss.xml" rel="self" type="application/rss+xml"/><item><title>Props That Make You Believe: Design Fiction</title><link>https://blog.bepuljang.co.kr/en/speculative-design/04/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/speculative-design/04/</guid><description>How do you make a future that doesn&apos;t exist yet feel real? This piece looks at the diegetic prototype borrowed from film props, how design fiction works, and how its language was absorbed into advertising.</description><pubDate>Mon, 09 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Talk about the future rarely feels real. A sentence that begins with “In 2040…” makes sense to the mind, but it doesn’t reach the body. In the previous piece, I talked about why we explore the outer edge of the cone, but if the futures we explore can’t be put in front of people, the debate never begins.&lt;/p&gt;
&lt;p&gt;This is where design fiction comes in. It’s about how to make people believe: for speculative design to work, it first needs a setting that makes the people who encounter the design believe in it.&lt;/p&gt;
&lt;h2 id=&quot;a-concept-borrowed-from-film-props&quot;&gt;A Concept Borrowed from Film Props&lt;/h2&gt;
&lt;p&gt;The term was popularized by the science fiction writer Bruce Sterling. He used it in his 2005 book &lt;em&gt;Shaping Things&lt;/em&gt;, and later defined it as “the deliberate use of diegetic prototypes to suspend disbelief about change.”&lt;/p&gt;
&lt;p&gt;The definition contains two unfamiliar expressions. “Suspension of disbelief” is the attitude we take when watching a film or similar medium: we know something doesn’t make sense, but we go along with it for a while. “Diegetic prototype” is a term from David Kirby, who studies the relationship between science and film. Diegesis means the world inside a story. So a diegetic prototype is a futuristic technology that appears as if it actually works within the world of the story.&lt;/p&gt;
&lt;p&gt;It’s easy to picture a scene where a character in a film uses some device casually. When it’s used like an everyday object, with no explanation and no wonder, the audience accepts the world in which it exists simply as a premise, before asking whether the technology is necessary or believable.&lt;/p&gt;
&lt;p&gt;In a 2009 essay, Julian Bleecker brings this idea into design practice. If films make us believe in the future through props, then designers, who are the people who actually make the props, can do the same. In other words, props can carry our imagination of the future.&lt;/p&gt;
&lt;h2 id=&quot;not-a-product-but-a-fragment-of-a-world&quot;&gt;Not a Product, but a Fragment of a World&lt;/h2&gt;
&lt;p&gt;The charm of design fiction is that it doesn’t paint a grand vision of the future directly. Instead, it makes a small, ordinary fragment that could plausibly have come out of that world.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;TBD Catalog&lt;/em&gt; by the Near Future Laboratory, which Bleecker co-founded, is a good example. It’s a booklet modeled on an in-flight shopping catalog, but instead of introducing future technologies, it presents itself as a product listing from a world where those technologies have already become commonplace. The moment an imagined technology appears not in an article that spotlights it but as an everyday discounted item, the viewer can take in the fragment of the future the designer imagined more realistically, and feel it.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://vimeo.com/107015105&quot;&gt;▶ Vimeo video&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;small&gt;Introduction to the &lt;em&gt;TBD Catalog&lt;/em&gt;, Near Future Laboratory (2014)&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;Another example, Sascha Pohflepp’s 2006 work &lt;em&gt;Buttons: Blind Camera&lt;/em&gt;, is an object that actually works. This camera has no lens. When you press the button, instead of taking a picture, it shows you a photo someone else took and shared at the same moment. Even before hearing any explanation, holding it in your hand makes you think about what it means to take a picture, and to own one.&lt;/p&gt;
&lt;p&gt;The principles common to these examples can be summed up in roughly three points.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Build a plausible context rather than perfect function.&lt;/strong&gt; Borrow everyday formats such as user manuals, receipts, and customer complaints.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Don’t make the technology the protagonist.&lt;/strong&gt; Show how people live alongside it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Leave just enough strangeness.&lt;/strong&gt; Too absurd, and people turn away; too familiar, and it reads as just another product mockup.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;focusing-on-the-question-not-the-technique&quot;&gt;Focusing on the Question, Not the Technique&lt;/h2&gt;
&lt;p&gt;But this method has a trap. The power to make people believe isn’t used only for asking questions. Unlike the intended purpose of raising questions and drawing out speculation, a misused technique can end up as a mere display of imagined futures, or as an advertisement.&lt;/p&gt;
&lt;p&gt;In a 2020 piece for MIT Technology Review, the journalist Tim Maughan traces this process. One of his examples is Corning’s 2011 &lt;em&gt;A Day Made of Glass&lt;/em&gt;. In this video, every pane of glass at home and on the street becomes a touchscreen. Its form resembles design fiction, but a critical stance is hard to find.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=6Cf7IL_eZ38&quot;&gt;▶ Corning&amp;#x27;s promotional video A Day Made of Glass&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;small&gt;&lt;em&gt;A Day Made of Glass&lt;/em&gt;, Corning (2011)&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;There’s an even more ironic case. In Superflux’s 2015 short film &lt;em&gt;Uninvited Guests&lt;/em&gt;, an elderly man outwits the smart devices that monitor him. Maughan points out that a 2019 Amazon Echo ad resembles this film, except that in the ad, the devices are welcomed. The same grammar delivers the opposite message.&lt;/p&gt;
&lt;p&gt;In the end, the test is probably what the prop leaves behind. If what remains after seeing it is “I want one,” it’s an ad; if what remains is “Is it really okay to live like this?”, it’s a question. Of course, there’s a wide gray zone between the two. But just as the palm and the back of the hand are close yet can never be the same, the two may look alike and cause confusion, but if we stay focused on the original purpose, questioning and speculating about the future, we can avoid falling into the trap.&lt;/p&gt;
&lt;p&gt;In the next piece, I’ll set theory aside for a while and look at actual works. Some slightly strange objects will appear, such as a battery charged by corpses and a clock that eats flies.&lt;/p&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Tim Maughan, &lt;a href=&quot;https://www.technologyreview.com/2020/02/21/905817/how-big-tech-hijacked-its-sharpest-funniest-critics/&quot;&gt;How big tech hijacked its sharpest, funniest critics&lt;/a&gt;, MIT Technology Review (2020)&lt;/li&gt;
&lt;li&gt;Julian Bleecker, “Design Fiction: A Short Essay on Design, Science, Fact and Fiction” (2009)&lt;/li&gt;
&lt;li&gt;Bruce Sterling, &lt;em&gt;Shaping Things&lt;/em&gt; (2005)&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>What is Speculative Design</category><category>Speculative Design</category><category>Design Fiction</category><category>Prototype</category><category>Futures</category></item><item><title>Factual and Value Judgments About the Future</title><link>https://blog.bepuljang.co.kr/en/speculative-design/03/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/speculative-design/03/</guid><description>The futures cone, a staple of explanations of speculative design, began as a tool of futures studies. Following the history of the diagram, this piece looks at how the &quot;preferred future&quot; differs from the other categories.</description><pubDate>Sun, 08 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;In the first piece of this series, I introduced the futures cone. It’s a diagram that sorts the futures extending from the present into probable, plausible, and possible ones, and proposes moving toward the preferred one among them. It also appears in nearly every explanation of speculative design.&lt;/p&gt;
&lt;p&gt;This diagram wasn’t made by designers. It’s a tool first used in futures studies, and looking into its history lets me restate, a little more accurately, what I passed over briefly in the first piece.&lt;/p&gt;
&lt;h2 id=&quot;the-futures-cone-and-its-history&quot;&gt;The Futures Cone and Its History&lt;/h2&gt;
&lt;p&gt;According to the history compiled by the Australian futurist Joseph Voros, Norman Henchey in 1978 divided futures into four kinds: possible, plausible, probable, and preferred. In 1990, Charles Taylor added a “cone of plausibility” whose range widens along a time axis. A picture close to the form we know emerged in 1994, when Trevor Hancock and Clement Bezold moved Henchey’s classification onto the cone. Voros began using it in his own foresight work around 2000 and went on to add more categories. So the cone we know took shape through many hands.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;A cone that widens over time from the present. The center line is the projected future, and from the inside out the cone is divided into probable, plausible, possible and preposterous futures. The preferred future is marked as points scattered across several zones.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1400&quot; height=&quot;780&quot; src=&quot;https://blog.bepuljang.co.kr/_astro/futures-cone-detail.CkoyHVN0_Z2fINmb.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;small&gt;The futures cone, redrawn after Hancock &amp;#x26; Bezold (1994) and Joseph Voros&lt;/small&gt;&lt;/p&gt;
&lt;h2 id=&quot;how-likely-is-it&quot;&gt;How Likely Is It&lt;/h2&gt;
&lt;p&gt;Looking more closely at the picture with this in mind, it becomes clear what this diagram really is.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Projected:&lt;/strong&gt; A future that simply extends the flow so far, predicted as is from the present moment: the so-called default future.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Probable:&lt;/strong&gt; A future that is “likely” to happen even without change. It usually rests on current trends.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Plausible:&lt;/strong&gt; A future that “could” happen, in light of how we currently understand the world.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Possible:&lt;/strong&gt; A future that “might” happen, depending on knowledge we don’t yet have.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Preposterous:&lt;/strong&gt; A future considered impossible for now.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The further in you go, the more certain a future is given current trends; the further out, the less likely it seems. In the end, these five categories sort futures by how plausible they seem.&lt;/p&gt;
&lt;p&gt;One caution: this judgment, too, is made by us, now. It isn’t rare for the preposterous future of one era to become the everyday life of the next. So the cone’s boundaries are less an objective partition of the world than a drawing of where our imagination currently stands.&lt;/p&gt;
&lt;h2 id=&quot;preference-lies-on-a-different-axis&quot;&gt;Preference Lies on a Different Axis&lt;/h2&gt;
&lt;p&gt;Now for the “preferred future.” Here lies the most important point of the diagram. Voros says that while the categories above are largely cognitive judgments about what is likely to happen, the “preferred future” is a value judgment about what ought to happen.&lt;/p&gt;
&lt;p&gt;That’s why the preferred future isn’t fixed to a particular zone of the cone. For one person, the default future may be the best; for another, a future that looks preposterous today may be the most desirable. Just as people’s values vary, preference can appear anywhere across the cone. On the other side are futures that shouldn’t happen.&lt;/p&gt;
&lt;p&gt;When introducing the concept in the first piece of this series, I explained that conceptual design puts its emphasis on what is likely to appear in reality, while speculative design leads toward the preferred future. That explanation alone can make the distinction confusing, but set against the two axes covered here, the difference becomes clear: one sits on the axis of probability and the other on the axis of value. They can seem alike in showing the future through the present, but they clearly have different purposes and concerns.&lt;/p&gt;
&lt;h2 id=&quot;what-changes-as-a-result&quot;&gt;What Changes as a Result&lt;/h2&gt;
&lt;p&gt;This distinction may look like wordplay, but it makes a practical difference. In meetings about the future, “this will probably happen” and “this is how it should be” blur together easily.&lt;/p&gt;
&lt;p&gt;“All customer support will be handled by AI” sounds like a forecast. But it often carries, slipped in alongside, a judgment that this would be a good thing. Borrow the form of a forecast, and a value judgment passes without debate. Something already decided isn’t a matter to be for or against; it simply becomes a premise of design, its environment.&lt;/p&gt;
&lt;p&gt;The futures cone lets us separate two questions. How likely is it? And, separately, should it happen? There are futures worth pursuing even if they’re unlikely, and futures worth stopping even if they’re likely. Speculative design often visits the outer edge of the cone not out of a taste for the absurd, but, we could say, to break the illusion that the default is the only future.&lt;/p&gt;
&lt;p&gt;So how can we bring a future that hasn’t arrived in front of people in a way that feels real? In the next piece, I’ll look at design fiction, a method often used for this.&lt;/p&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Joseph Voros, &lt;a href=&quot;https://thevoroscope.com/2017/02/24/the-futures-cone-use-and-history/&quot;&gt;The Futures Cone, use and history&lt;/a&gt; (2017)&lt;/li&gt;
&lt;li&gt;Trevor Hancock, Clement Bezold, “Possible futures, preferable futures”, Healthcare Forum Journal (1994)&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>What is Speculative Design</category><category>Speculative Design</category><category>Futures Cone</category><category>Futures Studies</category><category>Foresight</category></item><item><title>Design That Makes Questions Instead of Answers</title><link>https://blog.bepuljang.co.kr/en/speculative-design/02/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/speculative-design/02/</guid><description>Where did speculative design come from? Following critical design from the 1990s and Dunne &amp; Raby&apos;s A/B list, this piece looks at why designers began to doubt the premise that design is problem solving.</description><pubDate>Fri, 06 Nov 2026 01:25:06 GMT</pubDate><content:encoded>&lt;p&gt;In the previous piece, I introduced speculative design as “design that takes society and the age as a whole as its object, and imagines and proposes a better path.” But the concept didn’t start out with such grand ambitions. At its origin was a much pricklier question: is solving problems really all design has to do?&lt;/p&gt;
&lt;p&gt;For designers, problem solving is close to an identity. We find friction, analyze its causes, and propose a better form. I’ve written about UX design from that perspective myself, in pieces outside this series. Yet this frame holds a premise that’s easy to miss: the belief that the problem has already been correctly defined, and that the world as it stands has what it takes to accept the solution.&lt;/p&gt;
&lt;h2 id=&quot;the-designers-who-doubted-electronics&quot;&gt;The Designers Who Doubted Electronics&lt;/h2&gt;
&lt;p&gt;Among those who took this premise head-on were Anthony Dunne and Fiona Raby, in Britain. They had been developing the notion of “critical design” since the mid-1990s, and in his 1999 book &lt;em&gt;Hertzian Tales&lt;/em&gt;, Dunne treated electronic products as objects of aesthetic experience and critique.&lt;/p&gt;
&lt;p&gt;“Critical” here doesn’t mean finding fault. It’s closer to bringing to light the ways of life and values a product quietly assumes, so that we think about them again. The electronics we use don’t stop at performing functions; they also carry ideas about how we ought to live.&lt;/p&gt;
&lt;p&gt;Trace the roots further back and you reach the radical design and anti-design of 1970s Italy. Ettore Sottsass’s 1973 work &lt;em&gt;The Planet as a Festival&lt;/em&gt; is often cited as an early attempt. It showed that picturing another world, rather than an object that could be made right away, is also something design can do.&lt;/p&gt;
&lt;h2 id=&quot;ab-two-lists&quot;&gt;A/B: Two Lists&lt;/h2&gt;
&lt;p&gt;The most concise summary of this thinking is Dunne and Raby’s list, “A/B.” The preface to their 2013 book &lt;em&gt;Speculative Everything&lt;/em&gt; says the book grew out of it. The format sets familiar design on the left, under A, beside the design they propose on the right, under B.&lt;/p&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;A&lt;/th&gt;&lt;th&gt;B&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Affirmative&lt;/td&gt;&lt;td&gt;Critical&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Problem solving&lt;/td&gt;&lt;td&gt;Problem finding&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Provides answers&lt;/td&gt;&lt;td&gt;Asks questions&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Design for production&lt;/td&gt;&lt;td&gt;Design for debate&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Makes us buy&lt;/td&gt;&lt;td&gt;Makes us think&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;A word of caution when reading this table. It’s easy to take it as a claim that B is right and A is wrong, but the list works more like a tool for widening the range of what design can be through comparison. The world still needs door handles that work well, and that job belongs to A. It’s only when we assume A is all design can do that certain questions become impossible to raise.&lt;/p&gt;
&lt;h2 id=&quot;from-critique-to-speculation&quot;&gt;From Critique to Speculation&lt;/h2&gt;
&lt;p&gt;So how do critical design and speculative design differ? The boundary isn’t clean, and researchers draw it a little differently. Speculative design is often described as a branch of critical design, one focused on proposals about the future.&lt;/p&gt;
&lt;p&gt;The perspectives are similar, but the difference can be understood as one of tense. Critical design asks, “What world does this object assume right now?” while speculative design asks, “What if the world were different?” That is why the short phrase “what if” has become the grammar of this work.&lt;/p&gt;
&lt;p&gt;Dunne and Raby are clear that this “speculation” is different from “prediction.” Getting it right isn’t the goal. Instead, they put multiple possibilities in front of us to prompt debate about which futures we want and don’t want, and in doing so seek to discuss how reality isn’t fixed but can be changed, what a desirable future is, and what could be improved to meet it.&lt;/p&gt;
&lt;h2 id=&quot;the-use-of-uselessness&quot;&gt;The Use of Uselessness&lt;/h2&gt;
&lt;p&gt;Seen this way, it makes sense that the output of speculative design looks so useless. Unlike the common idea of design, you can’t buy it, it isn’t mass-produced, and it’s sometimes downright unpleasant. Its boundary with art is also blurry, which has drawn steady criticism that it can’t really be called design at all.&lt;/p&gt;
&lt;p&gt;But this view comes from an incomplete understanding of how the concept and essence of design have been expanding. The essence of design has steadily moved beyond “selling” toward the conceptual and speculative. What hasn’t changed through all of this is only that it is “goal-oriented”: that it “plans environments and behavior and brings about change.”&lt;/p&gt;
&lt;p&gt;In the end, a thing that has to sell can hardly go beyond what people already want. So only by stepping outside the market can we raise the question of what people ought to want, and only then does design turn from a tool for solving problems into a tool for rewriting them. In other words, this work follows design’s very nature, which puts the planning and improvement of environments and behavior first, and leaves even “selling” behind when it doesn’t serve the goal.&lt;/p&gt;
&lt;p&gt;In the next piece, building on this understanding, I’ll take another look at the “futures cone” I briefly introduced earlier.&lt;/p&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Anthony Dunne, &lt;em&gt;Hertzian Tales&lt;/em&gt; (1999)&lt;/li&gt;
&lt;li&gt;Anthony Dunne and Fiona Raby, &lt;em&gt;Speculative Everything&lt;/em&gt; (MIT Press, 2013)&lt;/li&gt;
&lt;/ul&gt;</content:encoded><category>What is Speculative Design</category><category>Speculative Design</category><category>Critical Design</category><category>Design History</category><category>Dunne &amp; Raby</category></item><item><title>For the You of a Few Months From Now — The Wiki, Your Records, and Teams</title><link>https://blog.bepuljang.co.kr/en/about-umtri/05/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/about-umtri/05/</guid><description>How to keep what the tree can&apos;t hold — why you built things the way you did — in the wiki, how to look back over your records, and how to work with a team, with prompt examples.</description><pubDate>Wed, 30 Sep 2026 15:58:04 GMT</pubDate><content:encoded>&lt;p&gt;A few months into a project, questions like these start to come up.&lt;/p&gt;
&lt;p&gt;“Why is checkout so complicated?”
“I think I tried this approach once — why did I stop?”
“When did we add this feature?”&lt;/p&gt;
&lt;p&gt;The tree is great at showing &lt;strong&gt;what is where&lt;/strong&gt;, but it can’t tell you &lt;strong&gt;why it was built that way&lt;/strong&gt;. In this last part we cover the &lt;strong&gt;wiki&lt;/strong&gt;, which keeps that “why”; how to look back over the records you’ve built up; and how to use Umtri with other people.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;you-a-few-months-from-now-are-a-stranger&quot;&gt;You, a few months from now, are a stranger&lt;/h2&gt;
&lt;p&gt;Even a project you built yourself feels unfamiliar after a few weeks. With vibe coding, where the AI wrote the code, even more so — you didn’t write it line by line.&lt;/p&gt;
&lt;p&gt;The same goes for the AI. Every new conversation, it knows nothing about the project’s history. So it may suggest an approach you already dropped because it caused problems.&lt;/p&gt;
&lt;p&gt;There’s one way to prevent this: &lt;strong&gt;write it down.&lt;/strong&gt; Since it’s hard for a person to keep notes every time, set things up so the AI writes and the person just reads.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;the-wiki-your-projects-encyclopedia&quot;&gt;The wiki: your project’s encyclopedia&lt;/h2&gt;
&lt;p&gt;Umtri’s &lt;strong&gt;wiki&lt;/strong&gt; is your project’s encyclopedia. It holds how each feature works, which rules must be kept, and what the words used in the project mean.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The AI writes the wiki; people read it.&lt;/strong&gt; On your ground page, click the &lt;strong&gt;Wiki&lt;/strong&gt; number in the Overall stats to open it.&lt;/p&gt;
&lt;h3 id=&quot;start-with-a-table-of-contents&quot;&gt;Start with a table of contents&lt;/h3&gt;
&lt;p&gt;The wiki is laid out so that reading it from the top gives you the whole project. An &lt;strong&gt;overview&lt;/strong&gt; sits at the top, chapters (concepts, architecture, operations, glossary and so on) sit under it, and entries sit under those. Pages are numbered like a book.&lt;/p&gt;
&lt;p&gt;At first, just ask for the table of contents. Umtri keeps recommended tables of contents for each kind of project (web service, app service, library), and the AI builds from those.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Set up the coffee-app wiki using the web service table of contents.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Fill in what you can tell for sure from the current code, and leave the rest empty.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;“Leave the rest empty” is the important part. A page filled with guesses is one you’ll trust without knowing it’s wrong.&lt;/p&gt;
&lt;h3 id=&quot;have-it-write-down-every-decision&quot;&gt;Have it write down every decision&lt;/h3&gt;
&lt;p&gt;The wiki really earns its keep &lt;strong&gt;the moment you make a decision.&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;We decided to apply the coupon once when the order is created, not at payment time.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Write that in the wiki, along with why (the bug where a retried payment applied it twice).&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;We used to check order status every second, but changed it to every five seconds to ease the load on the server. Update the wiki.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With that written down, when someone asks months later, “wouldn’t it be cleaner to move the coupon to the payment step?”, the wiki answers.&lt;/p&gt;
&lt;h3 id=&quot;have-the-ai-read-the-wiki-before-starting&quot;&gt;Have the AI read the wiki before starting&lt;/h3&gt;
&lt;p&gt;When the AI reads the wiki before new work, it’s far less likely to drift away from past decisions.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Read the payment pages in the coffee-app wiki, and add a refund feature that follows those rules.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Read the wiki from the top and summarize this project as if explaining it to someone seeing it for the first time.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;the-wiki-isnt-a-diary&quot;&gt;The wiki isn’t a diary&lt;/h3&gt;
&lt;p&gt;You don’t append “today I did this” to the wiki. It records &lt;strong&gt;how things are now&lt;/strong&gt;, so when something changes, the old sentence gets rewritten. In exchange, every edit keeps the previous version as a &lt;strong&gt;revision&lt;/strong&gt;, so you can always see what it used to say.&lt;/p&gt;
&lt;p&gt;So whenever you open the wiki, you get “the right answer as of now,” and the path that led there lives in the revisions and the tree’s records. Umtri teaches these rules to the AI, so you don’t need to worry about them.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;looking-back-over-your-records&quot;&gt;Looking back over your records&lt;/h2&gt;
&lt;p&gt;As the work from the earlier parts piles up, you get to see things like these.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The time slider&lt;/strong&gt; — replays how the project grew from the very beginning. Pick a season to watch just one stage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Commit records on each leaf&lt;/strong&gt; — click a leaf to see when, and through what work, that part changed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bug records&lt;/strong&gt; — resolved bugs keep “what we actually did.” If a similar problem comes back, start there.&lt;/p&gt;
&lt;p&gt;You can ask the AI, too:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Summarize what changed in coffee-app over the last month.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Check whether we had a similar bug around orders before, and tell me how we fixed it then.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Where are the most resolved bugs attached? Is there any structure worth reworking?&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;working-with-a-team&quot;&gt;Working with a team&lt;/h2&gt;
&lt;p&gt;A ground you’ve been using alone can be shared with others. Create a team under &lt;strong&gt;Settings → Organization&lt;/strong&gt; and invite teammates by email.&lt;/p&gt;
&lt;p&gt;There are three roles.&lt;/p&gt;





















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Role&lt;/th&gt;&lt;th&gt;Can do&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Viewer&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;See the tree, bugs, records and wiki&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Editor&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;All of the above + change the tree, bugs and wiki&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Owner&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;All of the above + ground settings, inviting and managing members&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Invite colleagues who don’t look at code, like planners or designers, as Viewers. The tree and the wiki alone let them see how far things have been built. For a developer joining the project, just ask them to read the wiki from the top.&lt;/p&gt;
&lt;p&gt;Your teammates’ AIs read the same tree and wiki. Whoever works with whichever AI, everyone works from the same map.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;During the beta, a team can have up to three people who can edit (Owner and Editor). There’s no limit on Viewers.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;wrapping-up-the-series&quot;&gt;Wrapping up the series&lt;/h2&gt;
&lt;p&gt;This series walked through Umtri in this order:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Seeing a project as a tree&lt;/li&gt;
&lt;li&gt;Connecting it to your AI&lt;/li&gt;
&lt;li&gt;Transplanting and seasons&lt;/li&gt;
&lt;li&gt;Planning, recording and handling bugs&lt;/li&gt;
&lt;li&gt;The wiki, your records, and teams&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Vibe coding’s strength is speed. To keep that speed months later, you need to be able to tell, at any time, “what this project looks like now” and “why it ended up this way.” Umtri helps the AI leave that behind as it works, without you having to keep notes yourself.&lt;/p&gt;
&lt;p&gt;If you haven’t tried it yet, start with the &lt;a href=&quot;https://app.umtri.io/demo&quot;&gt;demo&lt;/a&gt;. You can play with it right away, no sign-up needed.&lt;/p&gt;</content:encoded><category>Growing Your Project with Umtri</category><category>Umtri</category><category>Wiki</category><category>Team Collaboration</category><category>Vibe Coding</category></item><item><title>Recording as You Build — Plans, Saves and Bugs</title><link>https://blog.bepuljang.co.kr/en/about-umtri/04/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/about-umtri/04/</guid><description>Draw a new feature on the tree as a plan first, leave a record once it&apos;s built, and check how far a bug reaches when one shows up — with prompt examples.</description><pubDate>Fri, 31 Jul 2026 15:50:33 GMT</pubDate><content:encoded>&lt;p&gt;Once the tree is planted, the real work begins. A tree isn’t a picture you draw once; it’s only useful if it grows along with the project. This part covers three things you’ll do every day.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Before&lt;/strong&gt; building: draw the plan on the tree first&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;After&lt;/strong&gt; building: leave a record on the tree&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;When&lt;/strong&gt; something breaks: register a bug and see how far it reaches&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&quot;1-before-building-draw-the-plan-on-the-tree-first&quot;&gt;1. Before building: draw the plan on the tree first&lt;/h2&gt;
&lt;p&gt;Umtri has &lt;strong&gt;Plan Mode&lt;/strong&gt;. Branches and leaves drawn in plan mode are shown as &lt;strong&gt;plans that haven’t been built yet&lt;/strong&gt;. They aren’t recorded, so you can change them as many times as you like.&lt;/p&gt;
&lt;p&gt;Open the &lt;a href=&quot;https://app.umtri.io/demo&quot;&gt;demo&lt;/a&gt; and turn on plan mode. On the server trunk there’s a &lt;strong&gt;Notifications&lt;/strong&gt; branch (tell customers when their order is ready) drawn as a plan — the next feature, sketched ahead of time.&lt;/p&gt;
&lt;h3 id=&quot;have-the-ai-draw-the-plan&quot;&gt;Have the AI draw the plan&lt;/h3&gt;
&lt;p&gt;When you ask for a new feature, try having the AI draw a plan first instead of building right away.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;I want to notify customers when their coffee is ready.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Don&apos;t build it yet — draw it on the coffee-app tree as a plan first.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Show me which branches and leaves will appear under which trunk.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Seeing the AI’s plan on screen tells you at a glance whether it understood you. It’s much faster than reading a long explanation. If something looks off, just ask for a fix:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Notifications belong to the customer app, not the admin screen. Redraw the plan.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;hand-your-own-plan-to-the-ai&quot;&gt;Hand your own plan to the AI&lt;/h3&gt;
&lt;p&gt;It works the other way too: draw the plan yourself in the app and hand it to the AI. In plan mode, click a branch and use &lt;strong&gt;Add Plan Node&lt;/strong&gt; to add a leaf.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Read the Notifications branch I drew as a plan on the coffee-app tree and build it as drawn.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;When you&apos;re done, write the file path on each leaf and commit the plan.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Committing&lt;/strong&gt; a plan makes that branch a real part of the tree. A leaf needs an actual file written on it before it can be committed. That keeps branches that were only planned, never built, out of your tree.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;2-after-building-leave-a-record-on-the-tree&quot;&gt;2. After building: leave a record on the tree&lt;/h2&gt;
&lt;p&gt;When a feature is done, the tree should follow.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Add the coupon feature I just built to the coffee-app tree. Include the file paths.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The same goes for moving or renaming files while tidying up the code.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;I just moved the payment files into a payments folder. Update the file paths on the tree to match.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;I removed the order history screen. Clean it up on the tree too.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On a tree that has taken root, deleting a branch still leaves a trace. Replay it with the time slider later and you’ll see “there was a branch here, and then it went away.”&lt;/p&gt;
&lt;h3 id=&quot;attach-a-record-every-time-you-save-your-work&quot;&gt;Attach a record every time you save your work&lt;/h3&gt;
&lt;p&gt;Saving your work once with git is called a &lt;strong&gt;commit&lt;/strong&gt;. If you tell Umtri about each commit, every leaf whose file changed in that commit gets a note: “this is how it changed that day.” Click a leaf later and you’ll see when and how that part changed over time.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Record the commit I just made in Umtri too.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;tired-of-saying-it-every-time-make-it-a-rule-for-the-ai&quot;&gt;Tired of saying it every time? Make it a rule for the AI&lt;/h3&gt;
&lt;p&gt;Saying “add it to the tree” and “record the commit” every time gets old. Most AI tools read a rules file in your project folder first (&lt;code&gt;CLAUDE.md&lt;/code&gt; for Claude Code, &lt;code&gt;AGENTS.md&lt;/code&gt; for Codex). Write the rule there and the AI takes care of it.&lt;/p&gt;
&lt;p&gt;Umtri comes with a ready-made version of this rule, so all you need to say is:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Add the Umtri commit-sync rule to this project&apos;s CLAUDE.md.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After that, the AI checks on its own before every commit:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A new feature appeared → it adds a branch or leaf to the tree.&lt;/li&gt;
&lt;li&gt;Files moved → it fixes the file paths on the tree.&lt;/li&gt;
&lt;li&gt;This work fixed a bug → it marks the bug as Resolved.&lt;/li&gt;
&lt;li&gt;And last, it records the commit.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When something is unclear, the AI asks you. One question is better than a wrong branch.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;3-when-something-breaks-register-a-bug-and-see-how-far-it-reaches&quot;&gt;3. When something breaks: register a bug and see how far it reaches&lt;/h2&gt;
&lt;h3 id=&quot;connect-the-parts-first&quot;&gt;Connect the parts first&lt;/h3&gt;
&lt;p&gt;To know that “if this breaks, that breaks too,” the tree needs to say how parts are connected. There are two kinds of connection.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dependency (Edge)&lt;/strong&gt;: one part &lt;strong&gt;leans on&lt;/strong&gt; another. e.g. the order feature leans on the order storage.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;API&lt;/strong&gt;: one part &lt;strong&gt;calls&lt;/strong&gt; another. e.g. the checkout screen calls the server’s “create order.”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You don’t have to draw them yourself.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Look at the coffee-app tree and connect the parts that lean on or call each other in the code.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Connections are hidden by default. Turn on API and Edge under Layers in the sidebar to see them.&lt;/p&gt;
&lt;h3 id=&quot;see-how-far-it-reaches&quot;&gt;See how far it reaches&lt;/h3&gt;
&lt;p&gt;Click a leaf on the tree and press &lt;strong&gt;Show impact&lt;/strong&gt;: only the places affected if that leaf breaks stay bright, and everything else fades. Try the order storage (&lt;code&gt;orders&lt;/code&gt;) in the demo. You’ll see the impact run through the server that handles orders, payments, and the sales chart on the admin screen.&lt;/p&gt;
&lt;p&gt;You can also ask the AI:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;I&apos;m about to change checkout in coffee-app. What else should I check if I change it?&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;An &lt;strong&gt;empty&lt;/strong&gt; impact doesn’t mean “no impact.” It means “no connections written down yet.” Add the connections first.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;register-a-bug&quot;&gt;Register a bug&lt;/h3&gt;
&lt;p&gt;When you find a problem, register it as a bug. Bugs show up as red dots at their spot on the tree.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Register a bug in coffee-app: retrying a payment applies the coupon twice.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Also figure out which part is at fault and how serious it is.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the AI registers the bug, it also tells you what else needs checking because of it (the impact).&lt;/p&gt;
&lt;p&gt;Every bug gets a &lt;strong&gt;risk score&lt;/strong&gt; from 0 to 8, combining “how big a problem is it” with “how hard is it to fix.”&lt;/p&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Score&lt;/th&gt;&lt;th&gt;Meaning&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;0–1&lt;/td&gt;&lt;td&gt;A note or a wording fix. Nothing broken&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;Awkward to use&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3–4&lt;/td&gt;&lt;td&gt;A minor feature problem&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;5–6&lt;/td&gt;&lt;td&gt;A major feature problem&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;7–8&lt;/td&gt;&lt;td&gt;A critical problem&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Within each row, the harder it is to fix, the higher the number. The AI suggests a score, but if you see it differently, just ask for a change.&lt;/p&gt;
&lt;h3 id=&quot;fix-a-bug&quot;&gt;Fix a bug&lt;/h3&gt;
&lt;p&gt;Every bug gets a number like &lt;strong&gt;#1&lt;/strong&gt; or &lt;strong&gt;#2&lt;/strong&gt;. Calling it by number is handy.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Look at bug #1 and fix it.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Mark it &quot;Chasing&quot; when you start, then &quot;Resolved&quot; when you&apos;re done, and write down what you actually did.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A bug moves from &lt;strong&gt;Wild → Chasing → Resolved&lt;/strong&gt;. “Chasing” means “someone is already on this bug,” so it’s best to switch it the moment you start. It stops an AI in another chat, or a teammate, from grabbing the same bug twice.&lt;/p&gt;
&lt;p&gt;And &lt;strong&gt;writing down what you actually did&lt;/strong&gt; matters. When something similar happens months later, this is the first place you’ll look.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;todays-routine&quot;&gt;Today’s routine&lt;/h2&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;When&lt;/th&gt;&lt;th&gt;Say this&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Starting a new feature&lt;/td&gt;&lt;td&gt;”Don’t build it yet — draw it on the tree as a plan first”&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;The plan looks good&lt;/td&gt;&lt;td&gt;”Build it as planned, and commit the plan when you’re done”&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;You tidied up files&lt;/td&gt;&lt;td&gt;”Update the file paths on the tree too”&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;You found a problem&lt;/td&gt;&lt;td&gt;”Register it as a bug and tell me the impact”&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Just once&lt;/td&gt;&lt;td&gt;”Add the Umtri commit-sync rule to CLAUDE.md”&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The last part covers what all these records do for you months later, and how to keep what the tree can’t hold — “why we built it this way” — in the &lt;strong&gt;wiki&lt;/strong&gt;.&lt;/p&gt;</content:encoded><category>Growing Your Project with Umtri</category><category>Umtri</category><category>Plan Mode</category><category>Bugs</category><category>Vibe Coding</category></item><item><title>Planting Your First Tree — Transplanting and Seasons</title><link>https://blog.bepuljang.co.kr/en/about-umtri/03/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/about-umtri/03/</guid><description>Ask your AI to move a project you&apos;ve already built into a tree, split its growth into seasons, and take root to start recording — with prompt examples.</description><pubDate>Fri, 31 Jul 2026 06:44:23 GMT</pubDate><content:encoded>&lt;p&gt;Now that you’re connected, it’s time to plant the tree. This part covers asking your AI to move a project you’ve already built into a tree. If you’re starting a new project, the flow is the same, so just follow along.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;transplanting-bringing-in-a-tree-that-has-already-grown&quot;&gt;Transplanting: bringing in a tree that has already grown&lt;/h2&gt;
&lt;p&gt;When you first create a ground, the top of the screen shows &lt;strong&gt;Transplanting&lt;/strong&gt; in orange. It means you’re moving a tree that grew somewhere else onto new land.&lt;/p&gt;
&lt;p&gt;While transplanting, you can freely change or delete anything. You don’t need to get it perfect on the first try, so start relaxed.&lt;/p&gt;
&lt;p&gt;Open your AI tool &lt;strong&gt;in your project folder&lt;/strong&gt; and ask:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Look through this project&apos;s code and transplant it into my Umtri ground coffee-app as a tree.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Before planting, show me how you&apos;d split it into trunks and branches, and only plant once I say it&apos;s OK.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The “show me first” line matters. Looking at the structure the AI proposes, you can ask for changes like “make the admin screens their own trunk” or “put payments on a separate branch, not inside orders.” Shaping it in conversation before planting gives a much cleaner tree.&lt;/p&gt;
&lt;p&gt;Once the AI has planted it, open the ground in your browser. The tree will be there.&lt;/p&gt;
&lt;h3 id=&quot;ask-for-the-files-too&quot;&gt;Ask for the files too&lt;/h3&gt;
&lt;p&gt;It helps a lot later if every leaf on the tree says &lt;strong&gt;which file&lt;/strong&gt; it actually is. When you change a file, the record attaches to that leaf, and the AI immediately knows “to change this leaf, open this file.”&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;When you plant, write down the file path for each leaf too.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;check-that-it-moved-over-properly&quot;&gt;Check that it moved over properly&lt;/h3&gt;
&lt;p&gt;After transplanting, have the AI check its own work.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Read the coffee-app tree and check whether any features in the code are missing from the tree.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Tell me if any leaves are split too finely or attached to the wrong branch.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;seasons-splitting-growth-into-stages&quot;&gt;Seasons: splitting growth into stages&lt;/h2&gt;
&lt;p&gt;An Umtri tree grows inside &lt;strong&gt;seasons&lt;/strong&gt;. A season is a label for “which stage” of the project you’re in. It can be a version like &lt;code&gt;1.0&lt;/code&gt; or &lt;code&gt;2.0&lt;/code&gt;, or a stage name like &lt;code&gt;Pilot&lt;/code&gt; or &lt;code&gt;Launch&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The coffee-ordering app in the demo has three seasons.&lt;/p&gt;





















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Season&lt;/th&gt;&lt;th&gt;What happened&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;MVP&lt;/td&gt;&lt;td&gt;Browsing the menu, cart, placing orders&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Launch&lt;/td&gt;&lt;td&gt;Order status, login, coupons, the admin screen&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Growth&lt;/td&gt;&lt;td&gt;Sales chart, notifications (planned)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Only one season is &lt;strong&gt;now&lt;/strong&gt;; the rest are &lt;strong&gt;past&lt;/strong&gt;. When you open a new season, the one before it becomes past and can no longer be changed. It’s a way of drawing a line: “this was one stage.”&lt;/p&gt;
&lt;h3 id=&quot;ask-for-seasons-while-transplanting&quot;&gt;Ask for seasons while transplanting&lt;/h3&gt;
&lt;p&gt;If your project has been going for a few months, splitting its past into stages while you transplant makes the time slider much nicer to watch later.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Plant features built through the end of April in a &quot;Pilot&quot; season,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;and everything after that in a &quot;1.0&quot; season.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Use the git history to tell when each one was built.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the AI creates a season, it always asks you first. Opening a season is hard to undo, so it only happens after a person confirms. When the AI asks “Shall I create this season?”, check the name and answer.&lt;/p&gt;
&lt;p&gt;To make one yourself, click &lt;strong&gt;+ Add season&lt;/strong&gt; on an empty ground, or &lt;strong&gt;+ New Season&lt;/strong&gt; in the season list.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;taking-root-recording-starts-now&quot;&gt;Taking root: recording starts now&lt;/h2&gt;
&lt;p&gt;When the tree looks right, click the &lt;strong&gt;Root&lt;/strong&gt; button next to the orange label at the top to &lt;strong&gt;take root&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Here’s what changes once it takes root:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;From now on, every change to the tree is recorded.&lt;/li&gt;
&lt;li&gt;Deleting a branch doesn’t erase it completely; it leaves a trace that it was there.&lt;/li&gt;
&lt;li&gt;Past seasons can no longer be changed.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;Taking root &lt;strong&gt;can’t be undone.&lt;/strong&gt; Tidy things up while transplanting before you click it. Only the ground’s owner can click it; the AI can’t.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;if-youre-starting-a-new-project&quot;&gt;If you’re starting a new project&lt;/h2&gt;
&lt;p&gt;With no code yet, there’s nothing to transplant. Create a first season, and once your first feature starts working, ask:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Plant what we&apos;ve built so far in the coffee-app tree.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;From now on, whenever we add a feature, we&apos;ll add it to the tree too.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can take root right away. A new project has no past to bring over.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;replaying-with-the-time-slider&quot;&gt;Replaying with the time slider&lt;/h2&gt;
&lt;p&gt;Drag the &lt;strong&gt;time slider&lt;/strong&gt; at the bottom all the way left and you start from bare ground. Each step to the right sprouts branches and leaves in the order they appeared. Pick a season in the sidebar to see only what grew during that season.&lt;/p&gt;
&lt;p&gt;If your own tree is still small, try it on the &lt;a href=&quot;https://app.umtri.io/demo&quot;&gt;demo&lt;/a&gt; first. You can watch it grow across three seasons.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;The next part covers everyday life after planting: drawing a plan on the tree before building a feature, recording once it’s built, and checking how far a bug reaches when one shows up.&lt;/p&gt;</content:encoded><category>Growing Your Project with Umtri</category><category>Umtri</category><category>Project Structure</category><category>Transplanting</category><category>Vibe Coding</category></item><item><title>Connecting Umtri to Your AI</title><link>https://blog.bepuljang.co.kr/en/about-umtri/02/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/about-umtri/02/</guid><description>From signing up and getting a token to connecting Umtri to AI tools like Claude Code and checking that it worked — step by step.</description><pubDate>Thu, 30 Jul 2026 18:42:38 GMT</pubDate><content:encoded>&lt;p&gt;In the last part we saw that Umtri draws your project as a tree. This time we’ll &lt;strong&gt;connect&lt;/strong&gt; it, so the AI you use can read and draw that tree itself.&lt;/p&gt;
&lt;p&gt;It isn’t hard. Here’s the order:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Sign up for Umtri and make a place for your project&lt;/li&gt;
&lt;li&gt;Get a &lt;strong&gt;pass (token)&lt;/strong&gt; for your AI&lt;/li&gt;
&lt;li&gt;Copy the command shown on screen and paste it&lt;/li&gt;
&lt;li&gt;Talk to your AI to check&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&quot;how-do-the-ai-and-umtri-connect&quot;&gt;How do the AI and Umtri connect?&lt;/h2&gt;
&lt;p&gt;Out of the box, an AI tool can only see the code on your computer. There’s a way to let it use other services too, called &lt;strong&gt;MCP&lt;/strong&gt;. Think of it as installing an app on your phone — you’re adding a capability to your AI. (If you’re curious about MCP itself, &lt;a href=&quot;https://blog.bepuljang.co.kr/en/vibecoding/06&quot;&gt;Vibe Coding part 6&lt;/a&gt; covers it.)&lt;/p&gt;
&lt;p&gt;Once Umtri is connected, you can say things like:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Show me my Umtri grounds&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Read the project tree and tell me where the login feature lives&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Add the coupon feature I just built to the tree&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;1-sign-up-and-make-a-place-for-your-project&quot;&gt;1. Sign up and make a place for your project&lt;/h2&gt;
&lt;p&gt;Sign up at &lt;a href=&quot;https://app.umtri.io&quot;&gt;app.umtri.io&lt;/a&gt; with your email and a &lt;strong&gt;handle&lt;/strong&gt; (an ID that works like your name — it can’t be changed later). Click the verification link in the email and you’re in.&lt;/p&gt;
&lt;p&gt;Once you’re logged in, click &lt;strong&gt;New Ground&lt;/strong&gt; to make a place for your project. In Umtri, the land a single project grows on is called a &lt;strong&gt;ground&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;name&lt;/strong&gt; can be anything you like. (e.g. Coffee App)&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;slug&lt;/strong&gt; is the English name used in the address. (e.g. &lt;code&gt;coffee-app&lt;/code&gt;) It can’t be changed later, so keep it short and easy to recognize.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It’s fine if the tree is still empty. The AI will do the planting.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;2-get-a-pass-token-for-your-ai&quot;&gt;2. Get a pass (token) for your AI&lt;/h2&gt;
&lt;p&gt;For the AI to enter Umtri on your behalf, it needs a pass. This is called a &lt;strong&gt;token&lt;/strong&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Click your profile at the bottom of the left sidebar and go to &lt;strong&gt;Settings&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Choose &lt;strong&gt;API Tokens&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Give the token a name you’ll recognize. (e.g. &lt;code&gt;my laptop claude&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Leave the permission on the default, &lt;strong&gt;Write&lt;/strong&gt;. For the AI to draw the tree and not just read it, it needs Write.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Generate token&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;The token is shown &lt;strong&gt;only once&lt;/strong&gt;. Treat it like a password: don’t send it to anyone, and don’t put it in files that end up online. If you lose it, remove it from the list (Revoke) and make a new one.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;3-copy-the-command-and-paste-it&quot;&gt;3. Copy the command and paste it&lt;/h2&gt;
&lt;p&gt;As soon as you get a token, an &lt;strong&gt;ADD UMTRI MCP&lt;/strong&gt; box appears right below. Connection commands for each AI tool — Claude Code, Codex, Gemini CLI and more — are already written, with your new token inside.&lt;/p&gt;
&lt;p&gt;Click the button for your tool to copy it, paste it into the terminal (the black window where you run your AI tool), and press Enter. That’s it.&lt;/p&gt;
&lt;p&gt;There are two kinds of buttons.&lt;/p&gt;




















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Button&lt;/th&gt;&lt;th&gt;Meaning&lt;/th&gt;&lt;th&gt;Use it when&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Global&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Umtri is available in every project on this computer&lt;/td&gt;&lt;td&gt;It’s your own computer and you want convenience&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Local&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Umtri is available only in the project folder where you ran the command&lt;/td&gt;&lt;td&gt;You’d rather manage each project separately&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;If you’re not sure, pick &lt;strong&gt;Local&lt;/strong&gt;. It’s a little more work, but safer.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you click &lt;strong&gt;Dismiss&lt;/strong&gt; before copying, the command disappears along with the token. Always copy first.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;4-check-that-its-connected&quot;&gt;4. Check that it’s connected&lt;/h2&gt;
&lt;p&gt;Open your AI tool (if it was already open, close it and open it again) and say:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Show me my Umtri grounds&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the ground you just made shows up, you’re done.&lt;/p&gt;
&lt;p&gt;If you use Claude Code, you can also type &lt;code&gt;/mcp&lt;/code&gt; in the chat to see whether &lt;code&gt;umtri&lt;/code&gt; is connected.&lt;/p&gt;
&lt;h3 id=&quot;if-it-doesnt-work&quot;&gt;If it doesn’t work&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The AI says there’s no Umtri tool&lt;/strong&gt; — restart your AI tool. If you connected with Local, make sure you opened the AI in the same project folder where you ran the command.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The tools show up but keep failing&lt;/strong&gt; — the token was most likely entered wrong. The token isn’t checked when you connect, so even a wrong token looks connected. Get a new token and connect again.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reading works but adding or changing doesn’t&lt;/strong&gt; — the token may be Read-only. Make a new one with Write.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can ask the AI about this too:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;I don&apos;t think Umtri is connected properly. Can you check what&apos;s wrong?&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-changes-once-youre-connected&quot;&gt;What changes once you’re connected&lt;/h2&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Before&lt;/th&gt;&lt;th&gt;After&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Drawing the tree&lt;/td&gt;&lt;td&gt;One by one in the app&lt;/td&gt;&lt;td&gt;”Add it to the tree”&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Explaining the project&lt;/td&gt;&lt;td&gt;From scratch in every new chat&lt;/td&gt;&lt;td&gt;The AI reads the tree and starts from there&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Recording bugs&lt;/td&gt;&lt;td&gt;Separate notes&lt;/td&gt;&lt;td&gt;”Register it as a bug”&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;In the next part we finally plant the tree, starting with &lt;strong&gt;transplanting&lt;/strong&gt; — asking the AI to move a project you’ve already built into Umtri in one go.&lt;/p&gt;</content:encoded><category>Growing Your Project with Umtri</category><category>Umtri</category><category>MCP</category><category>Claude Code</category><category>Vibe Coding</category></item><item><title>What Kind of Tool Is Umtri?</title><link>https://blog.bepuljang.co.kr/en/about-umtri/01/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/about-umtri/01/</guid><description>Vibe coding is convenient, but it can leave you struggling to understand what you built yourself. Umtri gives you a way to visualize your project with your AI agent, so you can understand it at a glance.</description><pubDate>Wed, 29 Jul 2026 16:31:36 GMT</pubDate><content:encoded>&lt;p&gt;If you build apps by vibe coding, sooner or later you’ll hit moments like these.&lt;/p&gt;
&lt;p&gt;“Does our app even have this information? Where is it shown?”
“I want to tweak the checkout flow a little — how far does that reach?”
“When was this added? It got in without me noticing.”&lt;/p&gt;
&lt;p&gt;Compared with the days when you wrote the code yourself, these problems are simply easier to run into. And the bigger the project gets, the more this not-knowing piles up. When a user asks you a question, you can’t answer until your AI agent tells you. A change you thought was simple turns out to have a much wider blast radius than expected, and the service goes down for a long time. Sometimes information you didn’t even know about turns out to be exposed, and you end up in a tough spot.&lt;/p&gt;
&lt;p&gt;I felt this problem keenly while writing the &lt;a href=&quot;https://blog.bepuljang.co.kr/en/series/1&quot;&gt;Vibe Coding for Everyone&lt;/a&gt; series and working through countless projects. &lt;a href=&quot;https://umtri.io&quot;&gt;Umtri.io&lt;/a&gt; is the service built to solve it.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;drawing-your-project-as-a-tree&quot;&gt;Drawing your project as a tree&lt;/h2&gt;
&lt;p&gt;Umtri draws your project in the shape of a &lt;strong&gt;tree&lt;/strong&gt;. The core divisions of your project form the trunks (Trunk), and the flows branching from them grow from thick limbs (Limb) into thin twigs (Twig), then into leaves (Leaf) and leaf veins (Vein).&lt;/p&gt;



































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Part of the tree&lt;/th&gt;&lt;th&gt;Name&lt;/th&gt;&lt;th&gt;What it holds&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Thick trunk&lt;/td&gt;&lt;td&gt;Trunk&lt;/td&gt;&lt;td&gt;Service components (app, server, database, etc.)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Big branch&lt;/td&gt;&lt;td&gt;Limb&lt;/td&gt;&lt;td&gt;Major areas of activity (user menu, cart &amp;#x26; checkout, order history, etc.)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Small branch&lt;/td&gt;&lt;td&gt;Twig&lt;/td&gt;&lt;td&gt;Modules and groups of related information (the checkout process)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Leaf&lt;/td&gt;&lt;td&gt;Leaf&lt;/td&gt;&lt;td&gt;Objects (a checkout screen component, etc.)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Leaf vein&lt;/td&gt;&lt;td&gt;Vein&lt;/td&gt;&lt;td&gt;Functions and methods (actions inside a screen, like “Place order” or “Apply coupon”)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;These divisions aren’t absolute, because no two projects or components are alike. The only rule that always holds is that &lt;strong&gt;you go from big to small&lt;/strong&gt;. You don’t need to know the details. Ask the AI agent connected to Umtri to draw your project as a tree, and it draws the tree following those rules. All you do is judge whether the drawing is easy to understand, and ask it to redraw if it isn’t.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-does-a-tree-give-you&quot;&gt;What does a tree give you?&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;You see it all at once.&lt;/strong&gt; Without opening files one by one, you can see how your app is growing and which branch handles which information.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You can replay how it grew.&lt;/strong&gt; Drag the time slider at the bottom back and you’ll see the tiny first tree, then branches sprouting one by one. You can follow the order in which your information structure was created and how it grew.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You see how far a problem reaches.&lt;/strong&gt; As the tree is drawn, the connections and dependencies between its nodes are analyzed and recorded too. So when something breaks, the places you need to check alongside it and the scope of the impact are shown and recorded. You can check how a bug in that area — and its fix — affects other parts, and what to verify after the change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Your team and your AI agent see it too.&lt;/strong&gt; The tree is something your teammates and your AI agent can look at together, which makes communication clearer. It’s easy to share where things stand, and even an agent starting a fresh session can check the project’s map and find a safe path.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;take-a-look-first&quot;&gt;Take a look first&lt;/h2&gt;
&lt;p&gt;You can try it right away without signing up. Check out the &lt;a href=&quot;https://app.umtri.io/demo&quot;&gt;demo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;On the demo page, you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Click branches and leaves&lt;/strong&gt; — each node tells you what it is.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Drag the time slider at the bottom all the way left, then slowly to the right.&lt;/strong&gt; — branches sprout in the order the app grew.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Click a red dot (a bug)&lt;/strong&gt; — you’ll see where a bug like “the coupon is applied twice when payment is retried” is attached.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It’s not enough to really experience Umtri, but it shows you roughly how it works. Remember, though: the real value shows up once you connect it over MCP and manage your own project with it.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;when-should-you-start&quot;&gt;When should you start?&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Starting from scratch is best.&lt;/strong&gt; Create a project ground (Ground), then create your first season (Season). The season can be called PoC, or 0.0.1 — whatever you like. Add the very first thing you’re going to build as a trunk, and ask your agent to draw the plan you have in mind. Part 4 covers &lt;strong&gt;plan nodes&lt;/strong&gt; in detail.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;It’s fine if you already have a project, too.&lt;/strong&gt; Turn the time that has passed into seasons and ask your AI, season by season, to “look at the code and move it into a tree.” You can split by the commits in git, or by when files were created. A newly created ground starts in the &lt;strong&gt;transplanting&lt;/strong&gt; state, where you can edit without leaving a record. That lets you move an existing project over cleanly. Part 3 covers this as well.&lt;/p&gt;
&lt;p&gt;In the next part, we’ll connect Umtri to the AI you use (Claude Code and others). Umtri really shines when you tend the tree together with your agent over MCP. Try growth tracking, bug tracking, spec management with the wiki, and everything else project management needs — together with your agent.&lt;/p&gt;</content:encoded><category>Growing Your Project with Umtri</category><category>Umtri</category><category>Project Structure</category><category>Vibe Coding</category></item><item><title>MQTT: A Protocol Designed for Disconnection</title><link>https://blog.bepuljang.co.kr/en/musing/02/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/musing/02/</guid><description>While the web asks and waits for an answer, the world of things has been talking a different way. An introduction to publish/subscribe, and to the design philosophy of a protocol that even prepares a last will.</description><pubDate>Wed, 15 Jul 2026 03:53:47 GMT</pubDate><content:encoded>&lt;p&gt;The conversation we have grown used to on the web is request and response. The browser asks the server, “Give me this page,” and the server answers. Ask nothing, and nothing happens. This is perfectly natural when a person sits in front of a screen and wonders about something.&lt;/p&gt;
&lt;p&gt;Step into the world of things, though, and the story changes. Say you have two hundred temperature sensors hanging from a warehouse ceiling. For a server to ask them “what’s the temperature?”, it has to ask two hundred times — and it has no idea when to ask. Ask every minute and you have bothered 199 sensors that had nothing new to say. Ask every ten minutes and a freezer can thaw in between without anyone knowing. On top of that, these sensors run on batteries, their signal is weak, and they drop off constantly.&lt;/p&gt;
&lt;p&gt;MQTT was born in exactly that situation. In 1999, Andy Stanford-Clark of IBM and Arlen Nipper of Arcom built it to monitor oil pipelines across the desert over satellite links. Satellite is slow, expensive, and unreliable. So the protocol had to be light — and above all, it had to &lt;strong&gt;treat disconnection as normal&lt;/strong&gt;. That origin explains nearly every design decision in MQTT.&lt;/p&gt;
&lt;h2 id=&quot;dont-ask-announce&quot;&gt;Don’t ask. Announce.&lt;/h2&gt;
&lt;p&gt;MQTT’s first move is to stop asking. A sensor asks no one anything; it simply &lt;strong&gt;publishes&lt;/strong&gt; what it knows. “warehouse/freezer-a/temperature is -18.2.” That statement goes to an intermediary called a &lt;strong&gt;broker&lt;/strong&gt;. Anyone curious about that fact has already &lt;strong&gt;subscribed&lt;/strong&gt; with the broker: “Tell me when warehouse/freezer-a/temperature changes.”&lt;/p&gt;
&lt;p&gt;The sensor doesn’t know who is listening. The listeners don’t need to know which sensor spoke. The two are joined by nothing but a name — the &lt;strong&gt;topic&lt;/strong&gt;. A database recording temperatures, an alerting service, an admin dashboard: if all three subscribe to the same topic, the sensor still publishes exactly once. Not a single line of the sensor’s code changes.&lt;/p&gt;
&lt;p&gt;Topics form a hierarchy with slashes, and subscriptions can use wildcards.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;warehouse/freezer-a/temperature   ← just this one&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;warehouse/+/temperature           ← every freezer&apos;s temperature (+ is one level)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;warehouse/#                       ← everything under warehouse (# is the rest)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In code it comes to about this much. Once you know the broker’s address, the rest is two verbs.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mqtt &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;mqtt&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; client&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; mqtt.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;connect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;mqtt://broker.local&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// the listening side&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;client.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;subscribe&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;warehouse/+/temperature&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;client.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;on&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;message&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, (&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;topic&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;payload&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  console.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(topic, &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;String&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(payload)); &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// warehouse/freezer-a/temperature -18.2&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// the speaking side&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;client.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;publish&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;warehouse/freezer-a/temperature&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;-18.2&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;three-devices-for-handling-absence&quot;&gt;Three devices for handling absence&lt;/h2&gt;
&lt;p&gt;So far this is not far from any other publish/subscribe system. What let MQTT survive in the world of things comes next.&lt;/p&gt;
&lt;p&gt;First, &lt;strong&gt;QoS&lt;/strong&gt; — the level of delivery guarantee. Level 0 just throws the message (no confirmation that it arrived). Level 1 resends until it arrives (so it may arrive more than once). Level 2 adds another round trip so it arrives exactly once (and is correspondingly slower). For a temperature that will be superseded by a fresh reading a second later, 0 is plenty; a command like “open the door” needs 1 or 2. Rather than handing you reliability for free, MQTT lets you &lt;strong&gt;pay for it per message&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Second, the &lt;strong&gt;retained message&lt;/strong&gt;. The broker holds on to the last value for each topic. When you open the admin dashboard, there is no reason to stare at an empty screen for minutes until the next temperature report arrives. The moment you subscribe, the last known value lands. For any system that deals in state, this is a bigger convenience than it first appears.&lt;/p&gt;
&lt;p&gt;Third — and this is the most characteristically MQTT of them all — the &lt;strong&gt;Last Will and Testament&lt;/strong&gt;. When a device connects, it hands the broker its will in advance: “If I vanish without saying goodbye, announce ‘offline’ on this topic for me.” Later, when the device truly dies, loses power, or falls off the network, the broker notices the absence and publishes the will on its behalf.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;mqtt.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;connect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;mqtt://broker.local&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  will: { topic: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;warehouse/freezer-a/status&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, payload: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;offline&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, retain: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;designing-absence-into-the-system&quot;&gt;Designing absence into the system&lt;/h2&gt;
&lt;p&gt;The idea of a will is worth pausing on. Most protocols design what will be exchanged while the connection is alive. MQTT goes a step further and designs &lt;strong&gt;what will be said after the connection is gone&lt;/strong&gt;. Disappearance is treated not as an exception to handle but as an ordinary event.&lt;/p&gt;
&lt;p&gt;That is a lesson from the pipeline in the desert. Satellite links drop. That is not a bug to be fixed but a condition to be accepted. When you design on top of a condition instead of denying it, you tend to get a sturdier thing. That, I think, is why MQTT is still connecting factories, cars, and the light bulbs in our homes two decades on.&lt;/p&gt;
&lt;p&gt;Much of what we build is like this too. Users close the app halfway through, the signal dies in the subway, a payment stalls without an answer. Do we call those moments exceptions and hurry to paper over them — or do we assume from the start that they will happen, and prepare in advance what should be said when they do? MQTT chose the latter, and I suspect that choice is less a technical decision than a stance.&lt;/p&gt;</content:encoded><category>Notes</category><category>MQTT</category><category>IoT</category><category>Protocol</category><category>Networking</category></item><item><title>Expo and Your First Screen</title><link>https://blog.bepuljang.co.kr/en/react-native/02/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/react-native/02/</guid><description>The fastest way into React Native is Expo. We create a project, get a first screen running on a real device in your hand, and look at what separates Expo from the bare workflow — and why Expo is the recommended starting point today.</description><pubDate>Tue, 14 Jul 2026 13:33:05 GMT</pubDate><content:encoded>&lt;p&gt;Last time we pinned down what React Native is: the same React with the renderer swapped out for a native one. Now it’s time to actually move our hands. Today’s goal is a single thing — &lt;strong&gt;getting an app you made onto the screen of the phone in your pocket.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;On the web this step was featherweight. You put an HTML document on your machine or a server, open it in a browser, and you’re done. A mobile app is a different situation. An app runs on an OS, and because it has to be “installed” onto that OS to work, it comes with plenty of constraints. So “how do I build and ship what I made so it runs smoothly on the OS?” is a problem from the very first minute.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;bare-native-build-vs-expo&quot;&gt;Bare (Native) Build vs. Expo&lt;/h2&gt;
&lt;p&gt;There are broadly two ways to ship an app from a React Native project.&lt;/p&gt;
&lt;p&gt;One is the &lt;strong&gt;bare workflow&lt;/strong&gt;. You build and ship the iOS project and the Android project directly, each on its own. Which means installing Xcode on a Mac, plus Android Studio and a JDK, and setting up simulators and emulators. You need a working understanding of each OS — and in particular, when a native build fails, you end up wrestling with error messages from complex tools like Gradle or CocoaPods.&lt;/p&gt;
&lt;p&gt;The other is &lt;strong&gt;Expo&lt;/strong&gt;. Expo manages that native layer on your behalf. You write JavaScript; Expo supplies the native shell needed to run it, ready-made. You can start developing without Xcode or Android Studio, build for Android without owning an iPhone, and make an app without owning a Mac. Today the official React Native documentation itself points newcomers to Expo first. It does have a downside, though: because everything is wrapped by Expo once, you have to use the libraries Expo provides, and your control over the native layer is limited.&lt;/p&gt;
&lt;p&gt;That is exactly why “what if I need to touch native code deeply later?” is the natural worry that follows. There used to be a hard decision at that point — ejecting out of Expo entirely. Not anymore. When you need it, the path to opening up the native code and working alongside it is there. &lt;strong&gt;Starting with Expo doesn’t mortgage your future.&lt;/strong&gt; So begin with it, without agonizing over the choice.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;creating-the-project&quot;&gt;Creating the Project&lt;/h2&gt;
&lt;p&gt;If Node.js is installed, you’re ready. One line in a terminal creates the project.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;npx&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; create-expo-app@latest&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; my-app&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;cd&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; my-app&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;npx&lt;/code&gt; fetches and runs a package on the spot instead of installing it globally — the same feeling as &lt;code&gt;create-vite&lt;/code&gt; or &lt;code&gt;create-next-app&lt;/code&gt; on the web.&lt;/p&gt;
&lt;p&gt;Open the folder and familiar things look back at you. There’s a &lt;code&gt;package.json&lt;/code&gt;, there’s &lt;code&gt;node_modules&lt;/code&gt;, there are JavaScript files. This is where last post’s claim — “the React ecosystem carries over” — becomes concrete. The conspicuous difference is the &lt;code&gt;app/&lt;/code&gt; folder: the file structure inside it becomes the screen structure of your app. It’s similar to file-based routing on the web, and we’ll take navigation on properly in post 6.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;onto-a-real-device&quot;&gt;Onto a Real Device&lt;/h2&gt;
&lt;p&gt;Now start the dev server.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;npx&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; expo&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; start&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A QR code appears in the terminal. This is where things depart from the web entirely. &lt;strong&gt;Install the Expo Go app on your phone and scan that QR code.&lt;/strong&gt; A moment later, the app you just made is running in your hand. No Xcode, no emulator, no cable.&lt;/p&gt;
&lt;p&gt;Knowing &lt;em&gt;why&lt;/em&gt; this works will keep you calm when something breaks later. Expo Go is &lt;strong&gt;an app whose native shell is already built&lt;/strong&gt;. The dev server bundles up only the JavaScript we wrote and ships it over; Expo Go receives it and runs it inside itself. In other words, we didn’t build and install an app — &lt;strong&gt;we sent our JavaScript into a shell that was already installed.&lt;/strong&gt; That’s also why your computer and phone must be on the same Wi-Fi. If the scan doesn’t connect, nine times out of ten the network is split.&lt;/p&gt;
&lt;p&gt;Prefer a simulator or emulator? Press &lt;code&gt;i&lt;/code&gt; (iOS) or &lt;code&gt;a&lt;/code&gt; (Android) in the terminal — that path does require Xcode or Android Studio. Press &lt;code&gt;w&lt;/code&gt; and it opens in a browser, too. But don’t take comfort from the web preview looking fine. What we’re building is a native screen, and its true face has to be checked on a device.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;editing-the-first-screen&quot;&gt;Editing the First Screen&lt;/h2&gt;
&lt;p&gt;Open &lt;code&gt;app/index.tsx&lt;/code&gt; (or &lt;code&gt;.jsx&lt;/code&gt;), clear it, and try this.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { View, Text } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;react-native&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; default&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Home&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;View&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; style&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{{ flex: &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, alignItems: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;center&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, justifyContent: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;center&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }}&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Text&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;Hello, React Native.&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Text&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;View&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Save, and the phone screen changes. The &lt;strong&gt;Fast Refresh&lt;/strong&gt; you know from the web works here exactly the same: edit, save, see it immediately, with component state preserved wherever possible. This short round trip is a large part of what makes developing in React Native enjoyable.&lt;/p&gt;
&lt;p&gt;Everything the last post promised is already sitting in these few lines. We wrote &lt;code&gt;View&lt;/code&gt;, not &lt;code&gt;div&lt;/code&gt;. We wrapped the words in &lt;code&gt;Text&lt;/code&gt; rather than leaving them bare. The style went in as a JavaScript object, not a CSS file, and &lt;code&gt;flex: 1&lt;/code&gt; means “take all the remaining space.” Each of those is the subject of post 3 and post 4. For now, letting your eye get used to the shape is enough.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;where-people-get-stuck&quot;&gt;Where People Get Stuck&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Scanned the QR, nothing happens.&lt;/strong&gt; Usually the network. Check that computer and phone are on the same Wi-Fi. Shared office or café networks sometimes block device-to-device traffic; &lt;code&gt;npx expo start --tunnel&lt;/code&gt; routes around that.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Saved, but the screen won’t change.&lt;/strong&gt; Press &lt;code&gt;r&lt;/code&gt; in the terminal to reload. If that doesn’t do it, restarting the dev server is the faster path.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Works on web, broken on the device.&lt;/strong&gt; That’s normal. The web preview is a convenience, and the two environments render differently. What you see on the device is the truth.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;wrapping-up&quot;&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;What we did today is simple but load-bearing. We built &lt;strong&gt;the round trip where editing JavaScript shows up instantly on the device in your hand.&lt;/strong&gt; Every post from here runs on top of that loop — which means whatever you learn, you can see it on screen right away.&lt;/p&gt;
&lt;p&gt;Next time we take on the &lt;code&gt;View&lt;/code&gt; and &lt;code&gt;Text&lt;/code&gt; that slipped in above. In a world with no &lt;code&gt;div&lt;/code&gt; and no &lt;code&gt;span&lt;/code&gt;, what exactly is a screen made of — and why must every piece of text be wrapped in &lt;code&gt;Text&lt;/code&gt;?&lt;/p&gt;</content:encoded><category>React Native for Mobile Apps</category><category>React Native</category><category>Expo</category><category>Dev Environment</category><category>Mobile Apps</category></item><item><title>On the Risk of Making Every Judgment With Your Own Eyes Alone — Trust Only Your Own Sight, and the Organization Closes Its Eyes</title><link>https://blog.bepuljang.co.kr/en/startup-prince/07/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/startup-prince/07/</guid><description>The third post in Part Two of The Startup Prince. A founder&apos;s judgment is the asset that carried the company this far — but the moment it becomes the only standard for every decision, it shuts the organization&apos;s eyes. We look at why Machiavelli made the choice of ministers the first measure of a ruler&apos;s ability.</description><pubDate>Tue, 14 Jul 2026 13:33:05 GMT</pubDate><content:encoded>&lt;p&gt;The previous post was about moving an organization through understanding rather than orders — handing over context, sharing the standards of judgment, getting people to move even where the founder isn’t watching. But there’s a next gate that founders who make it this far often trip over. They share context diligently, and yet &lt;strong&gt;every decision is still made with their own eyes alone.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A founder’s judgment is an asset. The company exists because they saw an opportunity nobody else did, and it got this far because at countless forks their call was the right one. The trouble is that this very record of success breeds a dangerous conviction: &lt;strong&gt;“In the end, my judgment turns out to be the most accurate.”&lt;/strong&gt; The moment that sentence becomes the only standard inside the organization, the company can no longer grow beyond the size of one person’s field of vision.&lt;/p&gt;
&lt;h2 id=&quot;the-company-where-every-decision-passes-through-one-person&quot;&gt;The Company Where Every Decision Passes Through One Person&lt;/h2&gt;
&lt;p&gt;The symptom usually shows up as intervention. What the person responsible decided, the founder overturns. The final pick among design drafts, the last word on a hire, even the wording of a single line — everything ends up passing through the founder’s hands. And the founder reads this as diligence. They’re accountable for everything in the company, so of course they do it.&lt;/p&gt;
&lt;p&gt;But here’s what happens in an organization when this repeats. Instead of making their own calls, people gradually start &lt;strong&gt;guessing at what the founder would think.&lt;/strong&gt; Rather than using their expertise to build the best proposal, they build the proposal that will clear the founder’s taste. In meetings, they watch the founder’s face instead of voicing an opinion. On the surface the organization looks quiet and efficient. In reality, &lt;strong&gt;everyone has closed their eyes, and only the founder’s are open.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The most painful part is that the people the founder hired usually know their field better than the founder does. The company pays dearly to bring in experts, then overwrites that expertise with its own judgment at precisely the moments it was meant to be used. The company could have had ten pairs of eyes. It still runs on one.&lt;/p&gt;
&lt;h2 id=&quot;how-machiavelli-sizes-up-a-ruler-by-his-ministers&quot;&gt;How Machiavelli Sizes Up a Ruler by His Ministers&lt;/h2&gt;
&lt;p&gt;In &lt;em&gt;The Prince&lt;/em&gt;, Machiavelli takes the first measure of a ruler’s ability to be &lt;strong&gt;who he has chosen to keep around him.&lt;/strong&gt; If the ministers are capable and loyal, the ruler can be judged wise, because he recognized their ability and kept it intact. If the people around him are incompetent, the verdict is already in — that choice was itself the first blunder.&lt;/p&gt;
&lt;p&gt;What deserves attention is his division of minds into three kinds: &lt;strong&gt;the mind that understands on its own, the mind that recognizes what others have understood, and the mind that understands neither by itself nor through others.&lt;/strong&gt; The first, he says, is excellent; the second, good; the third, useless. The point worth holding onto is that &lt;strong&gt;the second belongs to the category of excellence, not incompetence.&lt;/strong&gt; Even if you cannot see through everything alone, if you can recognize when someone else has seen correctly and adopt it, you are a capable ruler.&lt;/p&gt;
&lt;p&gt;The question this puts to a founder is this. In straining to be the first kind of mind, am I &lt;strong&gt;closing off even the chance to exercise the second?&lt;/strong&gt; The compulsion to see everything best oneself often makes it impossible to recognize when someone else has seen better.&lt;/p&gt;
&lt;h2 id=&quot;judgment-is-an-asset--and-a-liability-when-its-the-only-standard&quot;&gt;Judgment Is an Asset — and a Liability When It’s the Only Standard&lt;/h2&gt;
&lt;p&gt;Let’s avoid a misreading. None of this means the founder should set judgment down and put everything to a vote. The company’s direction, what it will refuse to do, where it will place its bets — those remain the founder’s to decide. Just as a company that leans on borrowed strength is precarious (post 3), a company whose founder has abandoned their own judgment loses its heading.&lt;/p&gt;
&lt;p&gt;The distinction to draw is not about &lt;strong&gt;the authority to decide but the material the decision is made from.&lt;/strong&gt; What’s dangerous is not that the founder decides; it’s that the decision is assembled &lt;strong&gt;only from material their own eyes gathered.&lt;/strong&gt; A company where the founder’s judgment is the sole input knows only what the founder knows and looks only where the founder looks. On the day the founder is wrong, there is not a single mechanism inside the organization to catch it.&lt;/p&gt;
&lt;p&gt;And conviction hardens with time. If the founder’s calls keep getting adopted and the outcomes aren’t bad, that looks like proof the founder was right. But if no other judgment was ever tried in the first place, that record is &lt;strong&gt;not evidence that the founder’s eye was correct — only a record that no other eye ever got a turn.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;opening-a-second-pair-of-eyes&quot;&gt;Opening a Second Pair of Eyes&lt;/h2&gt;
&lt;p&gt;The method isn’t grand. It starts with not saying your own judgment first. The moment the founder leads with a conclusion, the discussion narrows into a review of the founder’s proposal. Hear the responsible person’s proposal first, ask why they judged that way, and then weigh whether their reasoning beats yours.&lt;/p&gt;
&lt;p&gt;Separating reversible decisions from irreversible ones helps too. If a thing can be undone, it’s better to let the responsible person’s call stand even when it differs from yours. If an organization never accumulates the experience of deciding by its own judgment and then seeing the result, the number of people who can actually judge will never grow. The decisions where the founder’s eye absolutely must prevail are fewer than you’d think.&lt;/p&gt;
&lt;p&gt;Above all, what’s required is &lt;strong&gt;not fearing the experience of finding your own judgment wrong.&lt;/strong&gt; Show one flash of discomfort in a room where your proposal lost, and the organization reads it exactly and closes its eyes again. Conversely, let people see the founder say once, “that call is better than mine,” and from then on they begin looking with their own eyes. In Machiavelli’s terms, that is the moment the second kind of mind — &lt;strong&gt;the ability to recognize when another has seen correctly&lt;/strong&gt; — starts working inside the organization.&lt;/p&gt;
&lt;p&gt;The question to check yourself against is simple. Over the past month, &lt;strong&gt;how many decisions went the way a team member judged rather than the way you did?&lt;/strong&gt; If nothing comes to mind, the organization has its eyes closed right now.&lt;/p&gt;
&lt;p&gt;The next post takes on the other face of this problem. However hard a founder tries to borrow other people’s eyes, what if the information reaching them has already been filtered and smoothed? It’s about the organization where bad news never makes it across the founder’s threshold.&lt;/p&gt;</content:encoded><category>The Startup Prince</category><category>Startup</category><category>Leadership</category><category>Decision-making</category><category>The Startup Prince</category></item><item><title>The Tools of Analysis — When the Data Still Hasn&apos;t Spoken</title><link>https://blog.bepuljang.co.kr/en/uxd-manifesto/11/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/uxd-manifesto/11/</guid><description>From Card Sorting to Affinity Diagrams, KJ Mapping, POV statements, and HMW questions — an introduction to the tools of analysis that read structure and meaning out of fragmented observation data.</description><pubDate>Tue, 14 Jul 2026 11:47:50 GMT</pubDate><content:encoded>&lt;p&gt;These are the tools you reach for when the data has piled up but you still don’t know what it means. Observation is an enjoyable, insight-rich process, but the observed data itself tends to be fragmented, local, and subjective. Individual problems get tangled up with collective ones, and a special case can look like a universal pattern. The tools of analysis exist to find structure among these fragments and to read meaning out of them.&lt;/p&gt;
&lt;p&gt;The tools introduced here aren’t tied to any particular project phase. Whenever you have data and that data still hasn’t spoken, you can reach for them.&lt;/p&gt;
&lt;h2 id=&quot;card-sorting&quot;&gt;Card Sorting&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“Which of the categories we already know does this data belong to?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is a method for placing new observations into classification criteria that already exist. For example, if a service already has defined categories like “membership,” “payment,” and “store,” you sort what you found during observation into those categories and build experiential insights from within them. Grouping new observations by screen or by feature, when a list of screens or features already exists, follows the same approach.&lt;/p&gt;
&lt;p&gt;Statistically, this is a top-down approach that classifies data against labels that already exist; in machine-learning terms, it corresponds to supervised learning.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Caveat&lt;/strong&gt;: An existing classification scheme can distort what you observe. Data that doesn’t fit the criteria gets forced in anyway, and new discoveries that fall outside the existing categories are easily ignored. If a lot of data resists classification, you should question the categories themselves.&lt;/p&gt;
&lt;h2 id=&quot;affinity-diagram&quot;&gt;Affinity Diagram&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“Is there structure among this data that hasn’t been discovered yet?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is a bottom-up method: you gather observations for which no classification criteria have been set yet, arrange them by similarity, and build insight as you define the resulting clusters. In machine-learning terms, this maps to unsupervised learning — specifically, cluster analysis.&lt;/p&gt;
&lt;p&gt;Because it lets you focus on the data itself, free of the project lead’s preconceptions or assumptions, it’s useful when you want to newly define a problem or a need.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Caveat&lt;/strong&gt;: The boundaries of a cluster depend on the analyst’s judgment. The same data can produce entirely different structures depending on who arranges it, so where possible, it’s best to have multiple people cluster independently and then compare results.&lt;/p&gt;
&lt;h2 id=&quot;kj-mapping&quot;&gt;KJ Mapping&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“Can the distance and relationships between data points be made visually apparent?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Named after the Japanese anthropologist Kawakita Jiro, this method is a more granular variant of the affinity diagram: it maps data by adjusting the distance between items according to their affinity to one another.&lt;/p&gt;
&lt;p&gt;Affinity diagramming and KJ mapping are often used interchangeably and treated as near-synonyms, but strictly speaking they differ. Diagramming is a broader category than mapping. KJ mapping is a mapping method — one that uses coordinates and distance — that diagrams based on affinity, so it’s best understood as one specific way of executing an affinity diagram.&lt;/p&gt;
&lt;p&gt;The word “map” here is worth paying attention to. When a technique calls itself a map, it means the two-dimensional X/Y coordinates carry meaning, and the distance or relative position between data points becomes the crux of the analysis.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Caveat&lt;/strong&gt;: The core of the process is turning each observation into data and calculating the distance between them to place them accordingly — and the nature and outcome of the resulting clusters depends entirely on how sound that data conversion and calculation are. If the preprocessing is weak, the map itself ends up distorted.&lt;/p&gt;
&lt;h2 id=&quot;preprocessing-the-data-how-to-write-a-card&quot;&gt;Preprocessing the Data: How to Write a Card&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“How do you refine fragmented observations into analyzable units?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Whether you’re doing card sorting or affinity diagramming, you first need a preprocessing step that refines your observations into data suitable for analysis. In the same vein as data preprocessing in statistics, you need to filter out and tidy up data that’s too much of an outlier, observations that are too vague, and cards that mix multiple pieces of content together.&lt;/p&gt;
&lt;p&gt;The recommended approach is to write each card as a descriptive statement with the observed user as the subject. This makes the content easier to grasp when you go on to map it, and it reduces confusion.&lt;/p&gt;
&lt;p&gt;Say, for example, you observed a user standing in front of a touchscreen kiosk who hesitates over which button to press while trying to place an order, then walks away. Instead of writing the card as “complex touchscreen kiosk UI,” write it as &lt;strong&gt;“I want to order, but I don’t know where to press.”&lt;/strong&gt; This makes the situation and the user’s emotion unmistakable. It also clarifies which category the card belongs to, and makes it much easier to develop into a cluster that stays in sync with the user’s emotional state.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Caveat&lt;/strong&gt;: When writing a descriptive card, it should carry the user’s voice, not the observer’s interpretation. “The UI is inconvenient” is the observer’s judgment; “I don’t know where to press” is the user’s experience.&lt;/p&gt;
&lt;h2 id=&quot;pov-point-of-view-statement&quot;&gt;POV (Point of View) Statement&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“Can the observation be converted into a single point of view?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A POV statement is a tool for compressing scattered observations and analysis results into one clear point of view. It takes the following form:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“[User] needs [need/desire], because [insight].”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;For example: “An older user using a kiosk alone for the first time needs clear guidance on the opening screen, because they believe that pressing the wrong button can’t be undone.”&lt;/p&gt;
&lt;p&gt;What this format forces you to do is pack three elements — the user (who), the need (what), and the insight (why) — into a single sentence. It serves as the bridge that converts a pattern found through analysis into a point of view you can actually design against.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Caveat&lt;/strong&gt;: If a POV statement is too broad (“the user needs a good experience”), you can’t find direction; if it’s too narrow (“the user needs a blue button”), it already presumes a solution. The insight clause is the crux — whatever follows “because” must be the real reason uncovered through observation, not an assumption.&lt;/p&gt;
&lt;h2 id=&quot;hmw-how-might-we-question&quot;&gt;HMW (How Might We) Question&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“Can the problem be reframed as a solvable opportunity?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;HMW is a question in the form “How might we ___?” that reframes an identified problem as a design opportunity. It often starts from a POV statement.&lt;/p&gt;
&lt;p&gt;Continuing the kiosk example: “How might we help first-time older users place an order without fearing mistakes?”&lt;/p&gt;
&lt;p&gt;The power of an HMW question lies in framing the problem in the language of possibility rather than constraint. Instead of stopping at the diagnosis “the kiosk is hard to use,” the “how might we” frame opens up a concrete direction to explore.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Caveat&lt;/strong&gt;: Just like a POV statement, an HMW question needs the right scope. “How might we satisfy every user?” is too broad, and “How might we make the text bigger?” already contains its own answer. A good HMW question should be open enough to invite solutions in several directions, while still staying anchored to a specific user and context.&lt;/p&gt;
&lt;h2 id=&quot;closing&quot;&gt;Closing&lt;/h2&gt;
&lt;p&gt;All of these tools point in the same direction: moving fragmented observations toward structured understanding. Card sorting places data within an existing structure; affinity diagramming discovers new structure from the data itself; POV and HMW convert that structure into language you can design against.&lt;/p&gt;
&lt;p&gt;But there’s something worth remembering. The structure of analysis exists to make the user’s voice come through more clearly — not to replace that voice. Get swept up in inspiration and intuition, and the whole process can end up as nothing more than forced rationalization. Only by setting aside preconceptions and predictions and staying focused on the data itself does observation turn into a living structure.&lt;/p&gt;
&lt;p&gt;The next post covers the tools of expression — the tools for sharing the understanding of the user gained through analysis with the team, such as personas and empathy maps.&lt;/p&gt;</content:encoded><category>The UX Design Manifesto</category><category>UX Design</category><category>Analysis Tools</category><category>Affinity Diagram</category><category>Card Sorting</category></item><item><title>Tools of Expression — How to Share the Invisible with Your Team</title><link>https://blog.bepuljang.co.kr/en/uxd-manifesto/12/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/uxd-manifesto/12/</guid><description>Understanding the user shouldn&apos;t stay locked inside one person&apos;s head. We look at five tools — Persona, Empathy Map, Scenario, User Journey Map, and Storyboard — for sharing the invisible experience of the user with the rest of the team.</description><pubDate>Tue, 14 Jul 2026 11:47:50 GMT</pubDate><content:encoded>&lt;p&gt;Observing, analyzing, and defining the user builds an understanding inside our own heads. But that understanding still lives only within one person. Design is never a solo act, so this understanding has to be shared with the rest of the team. The tools covered in this post are the ones we reach for when it’s time to share the user and their experience with teammates.&lt;/p&gt;
&lt;p&gt;These tools all have something in common: they make the invisible visible. A user’s motivations, emotions, context, journey — none of these can be seen directly. These tools give shape to what is otherwise invisible, so that the whole team can look at the same thing and move in the same direction.&lt;/p&gt;
&lt;h2 id=&quot;persona&quot;&gt;Persona&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“Who exactly are we designing for?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A persona is a fictional character built to represent a group of users. Basic facts — name, profile photo, occupation, age — are combined with personality, preferences, and goals to construct the identity of a single person. Not every one of these elements is mandatory. Start with the minimum information needed to picture the user, then keep filling it in while checking whether the persona is becoming a solid enough basis for design decisions.&lt;/p&gt;
&lt;p&gt;The tagline deserves special attention. It condenses the persona’s core concern or need into a single sentence, and it becomes a compass whenever there isn’t time to study the full persona during the design process. A single question — “Would someone who carries this sentence close to their heart enjoy this experience?” — is often enough to bring the user back to mind. The more carefully the tagline is crafted, the more consistently the insights from user observation carry through the rest of the process.&lt;/p&gt;
&lt;p&gt;Picture the Double Diamond: the persona sits exactly at the point where the two diamonds meet. It’s the condensed output of observation and definition, and at the same time the starting point for the experience design that expands outward from there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A caution.&lt;/strong&gt; A bad persona can throw an entire project into confusion. Never fabricate attributes irresponsibly just to fill every field of a template, use a name or celebrity photo that doesn’t fit the user group, or reverse-engineer a persona to match a scenario you already had in mind. When an attribute is uncertain, it’s better to leave it out and keep the persona lean.&lt;/p&gt;
&lt;p&gt;Above all, a persona without observation behind it is a fiction. A persona must be the distilled product of user observation and definition. A persona that appears out of nowhere severs the early half of the project from the later half, and undermines the entire point of going through a UX design process in the first place.&lt;/p&gt;
&lt;h2 id=&quot;empathy-map&quot;&gt;Empathy Map&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“What’s happening inside the user’s mind?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;An empathy map records the user’s experience across four quadrants.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Says&lt;/strong&gt;: Direct quotes and utterances captured during interviews or observation&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Thinks&lt;/strong&gt;: Inner thoughts, expectations, and questions the user didn’t voice but that surface anyway&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Does&lt;/strong&gt;: Concrete observed behaviors and behavioral patterns&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Feels&lt;/strong&gt;: The user’s emotional state — anxiety, anticipation, frustration, satisfaction, and so on&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If a persona sketches the user’s outer contour, an empathy map looks inward. Deep insight tends to emerge from the gap between Says and Thinks — the space between what the user says and what they’re actually thinking. If a user says “It’s fine, it’s usable” (Says) while pressing the same button a third time (Does), there’s something happening in between worth paying attention to.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A caution.&lt;/strong&gt; An empathy map is a tool for organizing observed data, not imagined data. What goes into all four quadrants has to come from actual observation and interviews. Thinks and Feels, in particular, involve the observer’s interpretation, so it’s worth building the habit of recording the observed facts that back up each interpretation.&lt;/p&gt;
&lt;h2 id=&quot;scenario&quot;&gt;Scenario&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“What kind of experience does the user go through, and in what context?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A scenario is a narrative account of the sequence of events a persona goes through to reach a goal in a specific situation. The key word is “narrative.” A scenario isn’t a feature list — it takes the shape of a story. A feature list tells you only the “what”; a scenario captures the “when, where, why, and how it felt” as well.&lt;/p&gt;
&lt;p&gt;“The user searches for a product and adds it to the cart” is a functional spec. “On the subway ride home, Jimin — trying to pick out a birthday gift for a friend’s party tomorrow — holds her phone in one hand and types ‘gift for man in his 30s’ into the search box. The results are overwhelming, and as she scrolls, the train jolts and she accidentally taps the wrong item…” is a scenario. The second version carries context, constraints, emotion, and the unexpected.&lt;/p&gt;
&lt;p&gt;Scenarios split by purpose. An As-Is scenario describes the experience the user currently goes through, exposing exactly where the problems occur. A To-Be scenario describes the improved future experience that the design aims to deliver, laying out the direction of change. Placing the two side by side makes it clear what the design is actually trying to change.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A caution.&lt;/strong&gt; A good scenario has to be rooted in a persona. The persona’s personality and tagline should be the basis for the behavior described in the scenario, and the flow of context and emotion needs to be faithfully reflected. A scenario disconnected from its persona is nothing more than the designer’s imagination.&lt;/p&gt;
&lt;h2 id=&quot;user-journey-map&quot;&gt;User Journey Map&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“Where does the overall flow of the experience break down?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A journey map is a scenario rendered visually. Time runs along the horizontal axis and the user’s emotion or satisfaction runs along the vertical axis, showing the ups and downs of the experience at a glance.&lt;/p&gt;
&lt;p&gt;The strength of a journey map is that it gives you a view of the whole rhythm. Things that are easy to miss when you’re absorbed in individual steps — a stretch of consecutive negative experiences, or a flat journey with no emotional high point — stand out clearly once they’re mapped.&lt;/p&gt;
&lt;p&gt;Two core concepts make up this map. A touchpoint is any moment where the user comes into contact with the product or service — seeing an ad, downloading the app, calling customer support — and at each one, the user forms an emotional reaction. A pain point is a moment where the user experiences discomfort or frustration: a place where the emotion curve drops sharply, and where design intervention is most urgently needed.&lt;/p&gt;
&lt;p&gt;The word “map” is worth dwelling on. In the earlier post on KJ Mapping, we noted that the word “map” carries the meaning of two-dimensional coordinates, distance, and relative position — and the same is true here. On the horizontal axis of time and the vertical axis of emotion, the coordinates of each point, and the distance and slope between points, all carry meaning.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A caution.&lt;/strong&gt; A beautifully drawn journey map that fails to reflect the user’s actual experience is nothing more than decoration. The value of a journey map lies not in visual polish but in accuracy grounded in observation.&lt;/p&gt;
&lt;h2 id=&quot;storyboard&quot;&gt;Storyboard&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“Can the whole team grasp the flow of this experience at a glance?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A storyboard expresses a scenario as a sequence of visual scenes, like panels in a comic strip. Each panel captures the user’s action, environment, and emotion through a simple drawing and a short caption.&lt;/p&gt;
&lt;p&gt;If a scenario is a story told in words, a storyboard is a story told in pictures. Things that are hard to convey through text alone — spatial arrangement, physical movement, the passage of time, the relationship between the user and their environment — come through intuitively in a visual scene. Reading about “a scene of searching one-handed on a jostling subway train” and seeing that scene drawn out simply land differently.&lt;/p&gt;
&lt;p&gt;A storyboard also reduces divergent interpretation in team communication. People reading the same scenario can each picture a different scene in their head, but a storyboard puts everyone in front of the same one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A caution.&lt;/strong&gt; What matters in a storyboard isn’t the polish of the drawings but the accuracy of scene selection. It should capture, as panels, the moments that matter most in the experience — where expectation forms, where a problem occurs, where an emotion shifts. Trying to draw every single step without exception only blurs the focus.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;The tools covered in this post — Persona, Empathy Map, Scenario, User Journey Map, and Storyboard — all do the same job: giving shape to something that is inherently invisible, the user’s cognition and emotion. Once something has shape, it can be shared. Once it can be shared, the team can align. And once the team is aligned, design moves forward on shared understanding instead of relying on any one person’s gut feeling.&lt;/p&gt;
&lt;p&gt;In the end, these tools exist for one reason. In a field that deals with something as invisible as user experience, they make the invisible visible and place it on a table where it can be discussed together.&lt;/p&gt;
&lt;p&gt;The next post moves on to “tools of implementation” — the tools that turn an idea still living in someone’s head into something you can hold in your hands, like paper prototypes and wireframes.&lt;/p&gt;</content:encoded><category>The UX Design Manifesto</category><category>UX Design</category><category>Persona</category><category>User Journey Map</category><category>Design Tools</category></item><item><title>Tools of Implementation — Making Ideas Tangible</title><link>https://blog.bepuljang.co.kr/en/uxd-manifesto/13/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/uxd-manifesto/13/</guid><description>A look at four tools that turn ideas into something users can actually experience, from paper prototypes to the Wizard of Oz technique.</description><pubDate>Tue, 14 Jul 2026 11:47:50 GMT</pubDate><content:encoded>&lt;p&gt;These are the tools you reach for when an idea needs to become something tangible.&lt;/p&gt;
&lt;p&gt;If the tools of expression turned the user into something the team could see, the tools of implementation turn a design idea into something the user can experience. Expression answers “who are we designing for,” while implementation tests “what are we trying to build.”&lt;/p&gt;
&lt;p&gt;There is a common misunderstanding about these tools. Because of the word “implementation,” it’s tempting to think of them as the final step on the way to a finished product. But the purpose of implementation here is not completion — it’s validation. These are tools for putting the hypothesis built through a scenario — the prediction that a given design will improve the user’s experience in a specific way — in front of real users. They need to be built fast, tested fast, and if necessary, discarded fast.&lt;/p&gt;
&lt;p&gt;These tools, too, aren’t tied to a single stage. Whether you’re at the moment of first conceiving an idea or revisiting a design that’s already somewhat developed, the right tool depends on the nature of the question you’re asking.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;paper-prototype&quot;&gt;Paper Prototype&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“Can we confirm whether this idea works, at the lowest possible cost?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A paper prototype is, quite literally, a prototype drawn on paper. Screen layouts, button placements, and the flow between pages are sketched by hand or printed and arranged, and a facilitator plays the role of the “system,” swapping screens in and out in response to the user’s actions.&lt;/p&gt;
&lt;p&gt;The core value of this tool is its extreme low cost. It takes only minutes to make, and revisions can be done instantly with a pencil and eraser. Its unfinished form also has the effect of focusing evaluators on structure and flow rather than appearance. A highly polished prototype tends to make users react to visual finish, while a sketch on paper keeps everyone focused on the core question: “does this flow make sense?”&lt;/p&gt;
&lt;p&gt;An earlier post on cognition discussed mental models — the internal conceptual maps users hold of a system. A paper prototype is also the fastest way to surface mismatches with that mental model. Catching the moment a user says “I thought pressing this button would take me somewhere else” before anything gets built digitally — that’s what a paper prototype does.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A caution.&lt;/strong&gt; The core of this tool is not trying to make it complete. The urge to draw neatly, or the compulsion to fill in every missing screen, kills the very advantage a paper prototype offers. A single rough sketch is often far better than an elaborately drawn diagram. When the facilitator plays the system, care must be taken not to lead the user — even something as small as reaching for a particular button first can contaminate the test.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;wireframe&quot;&gt;Wireframe&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“When you need to define a screen’s structure and the relationships between its elements”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A wireframe is the skeleton of a screen. No color, no imagery, no real copy — only layout, element placement, and flow. True to its name, it’s a “frame” built out of “wire.”&lt;/p&gt;
&lt;p&gt;What a wireframe tries to answer isn’t appearance, but structure: What elements need to be on this page? How does layout reveal their priority? What does the user see first on this screen? Which actions lead to which screens? To keep the focus on these questions, a wireframe deliberately strips away visual elements.&lt;/p&gt;
&lt;p&gt;A wireframe is also a shared language that lets developers, planners, and designers understand and discuss the same structure. If a paper prototype is a tool for exploring an idea, a wireframe is closer to a tool for sharing and refining the results of that exploration with the team.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A caution.&lt;/strong&gt; If discussion of visual design — colors, fonts, icon choices — starts creeping in at the wireframe stage, focus on structure gets derailed. The question this tool asks is “what needs to be here,” not “how should it look.” If wireframe review feedback includes “I don’t like this color,” that’s a sign the tool is being misused.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;interactive-prototype&quot;&gt;Interactive Prototype&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“When you need to draw out reactions from users that are close to how the real thing will feel”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;An interactive prototype is a mockup you can actually click or tap. Where a paper prototype or wireframe is a sequence of static screens, an interactive prototype responds like a digital system: pressing a button brings up the next screen, scrolling reveals content, and the interface reacts to the user’s actions.&lt;/p&gt;
&lt;p&gt;What this tool captures is “feel.” The speed of transitions between pages, the way screens change, the rhythm of interaction — none of this can be assessed from static screens. An interactive prototype gives users an experience close to using the actual product, and in doing so, draws out reactions that static tools can’t capture. It’s especially useful for checking whether affordances are working — whether a button is properly signaling that it can be pressed.&lt;/p&gt;
&lt;p&gt;From the standpoint of Fidelity, the closer an interactive prototype gets to High-Fidelity, the closer it comes to the real feel of use — but production cost rises accordingly. It’s important to calibrate Fidelity to the nature of the question being validated. Low Fidelity is enough to check whether the overall flow makes sense; high Fidelity is needed to assess the quality of detailed interactions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A caution.&lt;/strong&gt; A high-fidelity prototype breeds attachment in the person who built it. The feeling of “I put this much work into it, I don’t want to change it now” gets in the way of objective judgment. Users, too, tend to hold back criticism in front of a polished mockup. It’s worth remembering that a prototype isn’t a dress rehearsal for the finished product but a test sheet for a hypothesis — it exists to be broken.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;wizard-of-oz&quot;&gt;Wizard of Oz&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“When you need users to experience a system that hasn’t actually been built yet”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The Wizard of Oz technique is a method in which the user believes they’re interacting with a real system, while in fact a person, hidden from view, is behaving like the system and manufacturing its responses. Like the wizard behind the curtain pulling all the strings in the fairy tale, a facilitator performs the role of the system.&lt;/p&gt;
&lt;p&gt;This technique is especially valuable when you need to test a system that’s technically difficult or expensive to build. For example, before developing a voice recognition interface, a facilitator can manually process a user’s spoken command and swap the screen accordingly. Even before an AI recommendation system has been trained, a person can manually supply recommendation results and observe how users react. By validating the concept before technical implementation, this approach reduces the risk of pouring massive development cost into the wrong direction.&lt;/p&gt;
&lt;p&gt;A paper prototype is, in a broad sense, also a form of the Wizard of Oz technique — the act of a facilitator playing the system and swapping screens is the very same principle.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A caution.&lt;/strong&gt; The moment the wizard’s presence is revealed, the test falls apart. The speed, consistency, and predictability with which the facilitator produces responses must feel as natural as a real system, which requires thorough preparation and familiarity with the scenario beforehand. There will also be moments when a user’s next move is hard to predict, forcing the facilitator to improvise — and any inconsistency at that point can contaminate the test.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;The tools covered in this post sit along a single spectrum. A paper prototype handles an idea roughly, a wireframe defines its structure, an interactive prototype tests the actual feel of use, and the Wizard of Oz technique lets people experience something that doesn’t yet exist. Understanding this spectrum makes it possible to judge which tool best fits the question you’re asking right now.&lt;/p&gt;
&lt;p&gt;There’s one thing all of these tools have in common: their purpose is to “meet failure early.” The cost of discovering you were wrong on a piece of paper and the cost of discovering it after development is finished are incomparable. The tools of implementation are the mechanism that creates that difference in cost.&lt;/p&gt;
&lt;p&gt;The next post covers the “tools of validation” — the tools for confirming whether what’s been built actually works, such as usability testing and A/B testing.&lt;/p&gt;</content:encoded><category>The UX Design Manifesto</category><category>UX Design</category><category>Prototyping</category><category>Design Tools</category><category>User Testing</category></item><item><title>The Tools of Validation — Confirming Whether the Design Actually Works</title><link>https://blog.bepuljang.co.kr/en/uxd-manifesto/14/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/uxd-manifesto/14/</guid><description>From usability testing to A/B testing and quantitative scales, we look at five validation tools that check, in front of real users, whether a design actually works.</description><pubDate>Tue, 14 Jul 2026 11:47:50 GMT</pubDate><content:encoded>&lt;p&gt;These are the tools you reach for when you need to confirm whether a design actually works.&lt;/p&gt;
&lt;p&gt;If the tools of implementation gave a hypothesis its form, the tools of validation put that form in front of users and ask whether the hypothesis holds. This is the moment we come face to face with something this series has emphasized from the very beginning — users do not behave the way designers predict.&lt;/p&gt;
&lt;p&gt;Looking back, the journey of this series traces a single loop. We began by observing user behavior to infer cognition, and validation, too, closes the loop by observing user behavior. The difference is that our first observations were aimed at “what experience is the user having right now,” while the observations in validation are aimed at “what experience does the user have in front of the design we’ve proposed.” The fact that both the starting point and the destination are user behavior is a reminder, once again, of what this work is fundamentally about.&lt;/p&gt;
&lt;p&gt;The tools of validation, too, belong to no single stage. Validation doesn’t wait for a finished product — it can begin the moment you’re sitting across from someone with a single sheet of paper prototype. What matters isn’t the stage, but the recognition that “this hypothesis hasn’t been validated yet,” and the willingness to go check.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;usability-testing&quot;&gt;Usability Testing&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“Can users achieve their goals with this design?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Usability testing is a method of directly observing users as they actually use a design. The typical approach is to give users a specific task and watch them carry it out. You observe where they pause, where they get lost, and where they succeed.&lt;/p&gt;
&lt;p&gt;There’s a principle that has to be established first in usability testing: what’s being tested is the design, not the user. When a user fails to complete a task, that is a failure of the design, not a failure of the user. The moment this perspective gets inverted, the facilitator starts steering the user toward the “correct” answer, and the test loses its meaning.&lt;/p&gt;
&lt;p&gt;For this reason, facilitators must not help users at the moment they run into difficulty. That difficulty is itself the most honest signal pointing to where the design needs to improve. Questions that probe intent, like “What were you trying to do?” are fine, but statements that steer behavior, like “Try clicking this button,” contaminate the test.&lt;/p&gt;
&lt;p&gt;You don’t need a large number of users. According to research by Jakob Nielsen, around five users are enough to uncover the majority of major usability problems. Small, iterative rounds of testing are more efficient than one large panel.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Caution.&lt;/strong&gt; The more artificially controlled the test environment is, the wider the gap grows between it and real-world usage. Performing a task in a quiet, focused lab setting is a completely different experience from using a phone one-handed in a noisy café. Where possible, test in an environment that resembles the actual context of use, or interpret the results with the environment’s limitations in mind.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;think-aloud-protocol&quot;&gt;Think-aloud Protocol&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“What’s going on inside the user’s head?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The think-aloud protocol asks users to say out loud whatever comes to mind while they use a design. It has the user narrate their own experience — “What are you looking at right now?” “Why did you think that?”&lt;/p&gt;
&lt;p&gt;If behavioral observation shows “what” someone does, think-aloud reveals “why” they do it. The fact that a user stares at a button for a long time is something behavioral observation can capture, but whether that’s because they’re anticipating its function or because they don’t understand what the label means and are hesitating — that’s something only think-aloud can surface. It’s the most direct way to hear exactly where the gap lies between the mental model and the design.&lt;/p&gt;
&lt;p&gt;There are two variants of the think-aloud protocol. Concurrent think-aloud has users speak while performing the task, and retrospective think-aloud has them look back on their actions and narrate after the task is done. The former captures thoughts in real time but raises cognitive load; the latter doesn’t interfere with performance but risks reconstructed memory.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Caution.&lt;/strong&gt; People don’t say exactly what they’re thinking. When a researcher is nearby, users unconsciously try to construct explanations that sound more rational and coherent. Rather than taking the content of think-aloud at face value, pay attention to the gap between what’s said and what’s done. When someone says “I clicked this button — of course it does that,” but they actually clicked it three times — that gap is the insight.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;heuristic-evaluation&quot;&gt;Heuristic Evaluation&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“When you need to spot a design’s potential problems quickly, without users”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Heuristic evaluation is a method in which UX experts inspect a design against a predefined set of principles — heuristics. It differs from other validation tools in that no users are involved. It’s a form of expert review, using expert knowledge as the yardstick.&lt;/p&gt;
&lt;p&gt;The foundation of this method is Jakob Nielsen’s ten usability heuristics. These include visibility of system status, error prevention and easy recovery, consistency and standards, and minimizing cognitive load. Experts use these principles as criteria to systematically document which principle each element of the design violates.&lt;/p&gt;
&lt;p&gt;Recruiting real users and preparing sessions takes time. Heuristic evaluation, by contrast, can be carried out quickly by a small number of experts. It’s useful early in a project, or when budget and time are limited, for quickly filtering out obvious problems. It can also serve as a pre-screening step that makes later usability testing more efficient.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Caution.&lt;/strong&gt; Expert predictions can’t substitute for actual user behavior. A problem flagged by heuristic evaluation might not be a problem for real users at all, and conversely, users sometimes catch problems experts miss entirely. This tool should be used to complement usability testing, not replace it. Also, a single evaluator introduces bias, so where possible, it’s recommended to have three to five experts evaluate independently and then pool the results.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;ab-testing&quot;&gt;A/B Testing&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“When you need data to confirm which of two designs produces better results”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A/B testing exposes two versions of a design to real users at the same time and measures which one performs better. Users are randomly split into groups, each experiencing version A or version B, and quantitative metrics like click-through rate, conversion rate, and bounce rate are compared.&lt;/p&gt;
&lt;p&gt;The strength of this tool is that it measures the effect of a single variable while holding everything else constant. “Does the blue button get more clicks than the green one?” “Does the version without an image convert to purchase better than the one with an image?” — A/B testing answers these questions with data instead of impressions or intuition.&lt;/p&gt;
&lt;p&gt;A/B testing proves most useful on live services. Because it requires enough traffic to reach statistically meaningful results, it suits the incremental improvement of a product that already has users, more than the early stages of a project.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Caution.&lt;/strong&gt; A/B testing tells you “what” is better, but not “why” it’s better. Whether the blue button gets more clicks because of its color, its position, or some other factor is something this method alone cannot tell you. It’s also worth noting that a short-term metric like click-through rate doesn’t necessarily align with the long-term quality of the user experience — satisfaction, trust. Optimizing a metric and improving an experience are not always the same thing.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;quantitative-scales-sus-csuq-and-others&quot;&gt;Quantitative Scales (SUS, CSUQ, and others)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“When you want to measure usability as a number and compare it across versions or products”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Quantitative scales are survey-style tools that turn usability experience into a number. The most widely used are the System Usability Scale (SUS) and the Computer System Usability Questionnaire (CSUQ).&lt;/p&gt;
&lt;p&gt;SUS consists of ten items, and responses on a 1-to-5 scale for each item are converted through a formula into a score out of 100. A score of 70 or above is generally interpreted as acceptable, and 85 or above as excellent. Because it’s simple to administer and standardized, it’s useful for tracking change over time or comparing different products.&lt;/p&gt;
&lt;p&gt;What quantitative scales offer is an objective baseline. A number like “SUS score of 62” conveys the severity of a problem far more convincingly than an impression like “users struggled with it,” and a change to “SUS score of 78” after improvements makes the impact of those improvements unmistakably clear.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Caution.&lt;/strong&gt; Don’t lose sight of the context a score carries. The same SUS score of 72 carries different weight for a medical device than it does for a social media app. Quantitative scales also measure how users rate their experience, not what they actually went through. To understand the reasons behind a score, you need to pair it with qualitative methods like usability testing or interviews. A number is the start of a question, not the end of one.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;These tools ask the same question from different angles: is our design actually improving the user’s experience? Usability testing and the think-aloud protocol observe behavior and thought directly; heuristic evaluation filters issues in advance through an expert’s eye; A/B testing compares outcomes with data; and quantitative scales express results as numbers. None of them is complete on its own — it’s best to combine them according to the nature of the question at hand.&lt;/p&gt;
&lt;p&gt;And once more: validation is not an ending, but a link in the loop. What’s discovered in validation feeds back into observation and analysis, giving rise to new definitions, expressions, and implementations. The fact that this loop never stops is exactly why UX design is not a single project, but an attitude.&lt;/p&gt;
&lt;p&gt;Now it’s time to bring this series’ journey to a close. The next post looks back over every loop we’ve traveled through and returns to the question we started with — what is UX design?&lt;/p&gt;</content:encoded><category>The UX Design Manifesto</category><category>UX Design</category><category>Usability Testing</category><category>Validation</category><category>Research Methodology</category></item><item><title>Epilogue: Back to the User</title><link>https://blog.bepuljang.co.kr/en/uxd-manifesto/15/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/uxd-manifesto/15/</guid><description>As this series comes to a close, we return to the question of what UX design is, and trace once more where the answer ultimately leads.</description><pubDate>Tue, 14 Jul 2026 11:47:50 GMT</pubDate><content:encoded>&lt;p&gt;This series began with a single question. What is UX design.&lt;/p&gt;
&lt;p&gt;In the process of searching for an answer, we traced design’s history back to its roots, explored the headwaters of the river we call experience, and looked beneath the waterline of the iceberg we call cognition. We read clues out of the whirlpool of behavior, and followed a journey built on the scaffolding of frameworks — observing, defining, visualizing, writing scenarios, prototyping, testing.&lt;/p&gt;
&lt;p&gt;Looking back, all of this, in the end, has been heading toward one place. The user.&lt;/p&gt;
&lt;p&gt;The user is who we observed. The user is who we empathized with. The user is who our designs were meant to serve. Techniques can change, frameworks can evolve, and new tools arrive every year. But the disposition to understand the user and empathize with their experience is the one thing about UX design that does not change — its essence.&lt;/p&gt;
&lt;p&gt;So now it is time to return to the question this series first asked. What is UX design.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UX design is a self-declared commitment to address the parameters of the user experience by folding them into the design plan, and it is, in itself, the posture of designing from objectivity and observation rather than from intuition or inspiration.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This is the core thesis of this series. I hope reading this sentence now feels different from reading it the first time. If you have examined how the parameters of experience — cognition, behavior, emotion — actually work, and if you now have the frameworks and tools to work with them, then the weight of this declaration will have shifted.&lt;/p&gt;
&lt;p&gt;Please take note of the phrase self-declared. UX design is not a role assigned to you by someone else. It is a proclamation you make to yourself: that you will grasp the user’s experience objectively, and design on that basis. Where that declaration exists, UX design exists. And where it does not, no tool, however impressive, can rightly be called UX design.&lt;/p&gt;
&lt;p&gt;This posture matters because UX design is, at its core, a discipline of humility.&lt;/p&gt;
&lt;p&gt;The designer does not rely on their own intuition or inspiration, but learns from the user’s behavior. They do not assume they are right, but test through prototypes. They do not try to get it perfect on the first attempt, but refine through iteration. All of this rests on a single premise: that I may not fully understand the user.&lt;/p&gt;
&lt;p&gt;And it is precisely that premise which, paradoxically, lets us move closer to the user.&lt;/p&gt;
&lt;p&gt;As this series has repeated throughout, I hope you won’t feel bound to any particular technique. You don’t need to build a persona, draw a journey map, or follow the Double Diamond. What matters is only this: standing before the user and trying to see the world through their eyes; trying to read, in their behavior, what they cannot put into words; and trying, on the strength of that understanding, to make the experience even a little better. Hold on to just that, and you are already doing UX design.&lt;/p&gt;
&lt;p&gt;The river of experience never stops flowing. And our effort to shape its course cannot end with a single project either — it must continue. Technology changes. Users change. Society changes. The one thing that does not change, through all of it, is the will to make the user’s experience better.&lt;/p&gt;
&lt;p&gt;Back to the user, once more.&lt;/p&gt;</content:encoded><category>The UX Design Manifesto</category><category>UX Design</category><category>User Experience</category><category>Design Philosophy</category><category>UX Design Manifesto</category></item><item><title>What Is React Native?</title><link>https://blog.bepuljang.co.kr/en/react-native/01/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/react-native/01/</guid><description>React Native is not a hybrid app wrapped in a WebView. Starting from the idea that React&apos;s way of thinking stays put while only the &apos;what to draw&apos; changes, we map out what React Native actually is — and where the web ends.</description><pubDate>Mon, 13 Jul 2026 15:59:47 GMT</pubDate><content:encoded>&lt;p&gt;React and React Native. When you first come across them, the relationship between the two can be confusing. How exactly do they differ? Put simply, you could say React builds for the web while React Native builds apps. But React Native buys itself a lot of misunderstanding. “Isn’t it just the web dressed up as an app?” “Isn’t there a WebView in there somewhere?” — that talk is still circulating, and older write-ups still say exactly that. So this first post isn’t about how to build something. It’s about pinning down &lt;strong&gt;what React Native actually is&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;react-doesnt-know-how-to-draw-a-screen&quot;&gt;React doesn’t know how to draw a screen&lt;/h2&gt;
&lt;p&gt;That sounds odd, but it’s true: React itself knows nothing about the DOM.&lt;/p&gt;
&lt;p&gt;In Part 1 we said the JSX a component returns isn’t a screen — it’s &lt;strong&gt;a blueprint of what the screen should look like&lt;/strong&gt;. Taking that blueprint and turning it into an actual screen is the job of something else entirely: a &lt;strong&gt;renderer&lt;/strong&gt;. On the web, that job belonged to &lt;code&gt;react-dom&lt;/code&gt;, the package we always installed alongside React.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// Web: react-dom turns the blueprint into real DOM nodes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { createRoot } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;react-dom/client&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which invites an idea. &lt;strong&gt;If the side that makes the blueprint (React) is separate from the side that draws it (the renderer), couldn’t we just swap out the drawing side?&lt;/strong&gt; Build a renderer that paints native iOS and Android UI instead of the DOM, and the screens you declared in React become the screens of a mobile app.&lt;/p&gt;
&lt;p&gt;That’s React Native. It is not a React-flavored imitation of React — it is &lt;strong&gt;the same React with a different renderer plugged in&lt;/strong&gt;. That sentence is the premise of this entire series. Every component, prop, piece of state, &lt;code&gt;useEffect&lt;/code&gt;, and Context you already know carries over intact.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;not-a-webview--genuinely-native&quot;&gt;Not a WebView — Genuinely Native&lt;/h2&gt;
&lt;p&gt;Let’s cut off the most common misunderstanding right away. React Native &lt;strong&gt;does not render your screen inside a WebView.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The confusion exists because a genuinely different approach does exist under similar branding. Cordova and Ionic-style tools open a browser view inside an app shell and render HTML in it. That’s what “hybrid app” means, and that’s why the “it’s really just the web inside” complaint attaches to them.&lt;/p&gt;
&lt;p&gt;React Native is not that. When you write &lt;code&gt;&amp;#x3C;View&gt;&lt;/code&gt;, it becomes a &lt;strong&gt;real native view object&lt;/strong&gt; — a &lt;code&gt;UIView&lt;/code&gt; on iOS, an &lt;code&gt;android.view.ViewGroup&lt;/code&gt; on Android. &lt;code&gt;&amp;#x3C;Text&gt;&lt;/code&gt; becomes the platform’s actual text view; scrolling is handled by the scroll view that platform has always used. The button on screen isn’t a browser’s imitation of a button. It’s the one the OS drew.&lt;/p&gt;
&lt;p&gt;Which is why a React Native app has no &lt;code&gt;div&lt;/code&gt;, no &lt;code&gt;span&lt;/code&gt;, no HTML, and no CSS file. Web technology wasn’t smuggled inside the app. &lt;strong&gt;Only React’s way of thinking made the trip; the materials are all native.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;how-javascript-and-native-talk-to-each-other&quot;&gt;How JavaScript and Native Talk to Each Other&lt;/h2&gt;
&lt;p&gt;Our component code is JavaScript. The thing actually drawing the screen is native. Something has to connect them.&lt;/p&gt;
&lt;p&gt;Older React Native called that connection the &lt;strong&gt;Bridge&lt;/strong&gt;. The JavaScript side and the native side couldn’t call each other directly; they serialized messages into JSON, put them on a queue, and passed them across asynchronously. It was closer to dropping a letter in a mailbox and waiting for a reply — and when the messages piled up, the screen answered a beat late. When you see an old article saying “React Native communicates over a bridge,” you’re reading a relic of that era.&lt;/p&gt;
&lt;p&gt;Not anymore. The structure was replaced by what’s called the &lt;strong&gt;New Architecture&lt;/strong&gt;, and it has been the default since version 0.76. Three names are enough:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;JSI (JavaScript Interface)&lt;/strong&gt; — the layer that lets JavaScript &lt;strong&gt;call native objects directly&lt;/strong&gt;. Less mailing a letter, more picking up the phone. This is the space where the mailbox used to sit.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fabric&lt;/strong&gt; — the new rendering system. Layout calculation and screen updates are far more tightly coupled than before, which removes a class of timing problems like flicker during scrolling.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TurboModules&lt;/strong&gt; — native capability modules (camera, location, and friends) are &lt;strong&gt;loaded when needed&lt;/strong&gt; rather than all hoisted at app startup. The app opens faster.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You don’t need the internals of any of the three right now. What you do need is to &lt;strong&gt;drop the stale impression that “JavaScript reaches native slowly, by detour.”&lt;/strong&gt; In practice, the performance problems you’ll actually hit come not from this channel but from drawing lists badly — which is what post 5 is for.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;whats-different-from-react&quot;&gt;What’s Different From React&lt;/h2&gt;
&lt;p&gt;For someone who already knows React, the most useful map is the one that shows which ground is already familiar.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Unchanged&lt;/strong&gt; — assembling screens out of components, JSX syntax, passing values down through props, holding state with &lt;code&gt;useState&lt;/code&gt; and triggering re-renders, &lt;code&gt;useEffect&lt;/code&gt;, &lt;code&gt;useRef&lt;/code&gt;, Context, and the npm ecosystem. Nothing you learned in Part 1 goes to waste.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What changes&lt;/strong&gt; is precisely the material the screen is made of.&lt;/p&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Web&lt;/th&gt;&lt;th&gt;React Native&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;div&lt;/code&gt;, &lt;code&gt;span&lt;/code&gt;, &lt;code&gt;p&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;View&lt;/code&gt;, &lt;code&gt;Text&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;CSS files, classes&lt;/td&gt;&lt;td&gt;&lt;code&gt;StyleSheet&lt;/code&gt; (a JavaScript object)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;URLs and routers&lt;/td&gt;&lt;td&gt;Navigation (a screen stack)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;onClick&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;onPress&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Browser APIs&lt;/td&gt;&lt;td&gt;Native APIs + permission requests&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Two tripwires are worth naming in advance, because nearly everyone catches a foot on them. First, &lt;strong&gt;every piece of text must live inside a &lt;code&gt;&amp;#x3C;Text&gt;&lt;/code&gt;.&lt;/strong&gt; Writing &lt;code&gt;&amp;#x3C;View&gt;some words&amp;#x3C;/View&gt;&lt;/code&gt;, web-style, is an error. Second, &lt;strong&gt;styles don’t cascade, and Flexbox’s default axis is vertical, not horizontal.&lt;/strong&gt; Bring your web habits over unchanged and the layout will almost certainly come out wrong. Posts 3 and 4 take these on properly.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;can-you-get-away-without-knowing-the-native-platform&quot;&gt;Can You Get Away Without Knowing the Native Platform?&lt;/h2&gt;
&lt;p&gt;“One codebase, two platforms” is true. It does &lt;strong&gt;not&lt;/strong&gt; mean “you never have to know anything about native.”&lt;/p&gt;
&lt;p&gt;Build an app for a while and the differences between iOS and Android come find you. Permission flows differ. A hardware back button exists on one and not the other. Dodging the notch at the top and the home indicator at the bottom works differently. React Native doesn’t erase those differences — &lt;strong&gt;it hands you a handle for dealing with them.&lt;/strong&gt; And now and then, no library exists for what you need, and you’ll be wiring up a native module yourself.&lt;/p&gt;
&lt;p&gt;Even so, it’s often the right call. You write most of your screens and business logic once, you reuse the React knowledge your team already has, and you get a development loop where an edit shows up in the app almost immediately. But if you’re building something driven by heavy graphics, or you need each platform’s newest capabilities the day they ship, native may still be the honest answer. A tool isn’t universal; it just has places where it fits.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;where-were-going&quot;&gt;Where we’re going&lt;/h2&gt;
&lt;p&gt;Three sentences, then. &lt;strong&gt;React’s way of thinking carries over untouched. The screen is drawn with real native views, not a WebView. But the DOM and CSS as materials are left behind, and new ones take their place.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Next time we’ll set up a development environment with Expo and get a first screen running on a real device. After that: native components, StyleSheet and Flexbox, lists and touch, navigation, platform APIs, and shipping to the stores. You learned React with a screen in front of you — this time, let’s draw the one that lives in your pocket.&lt;/p&gt;</content:encoded><category>React Native for Mobile Apps</category><category>React Native</category><category>Mobile Apps</category><category>Cross-platform</category><category>Native</category></item><item><title>Tools of Observation — When You Don&apos;t Know What Users Are Doing</title><link>https://blog.bepuljang.co.kr/en/uxd-manifesto/10/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/uxd-manifesto/10/</guid><description>Introducing eleven observation tools to reach for when you can&apos;t tell what users are doing or why. From Shadowing to ZMET, we look at each tool&apos;s use and its limits.</description><pubDate>Mon, 13 Jul 2026 15:59:47 GMT</pubDate><content:encoded>&lt;p&gt;These are the tools you reach for when you don’t know what users are doing.&lt;/p&gt;
&lt;p&gt;We’ve talked about the iceberg of cognition, and about the vortex of behavior. Consciousness, the part that surfaces, is only a fraction of the whole, and the real substance of what a user actually experiences is buried in unconscious behavior — that was the claim. So how do you capture that behavior? Every tool introduced here starts from the same question: “What is the user doing right now, and why are they doing it?”&lt;/p&gt;
&lt;p&gt;None of these tools belong to a single project phase. You can use them early on to discover problems, bring them back mid-design to test hypotheses, or deploy them after launch to trace unexpected usage patterns. The moment a question comes to mind is the moment to use one of these tools.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;shadowing&quot;&gt;Shadowing&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“In what actual order, and in what context, does this user act?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A method where you select a specific user and follow their every move as they perform a target task. The observer becomes the user’s shadow, recording the flow of behavior as it unfolds in their environment, unaltered.&lt;/p&gt;
&lt;p&gt;The key, as the name implies, is to become a shadow. The observer’s greatest enemy is the Observer Effect — the fact that the act of observation itself influences the user being observed. The moment a user becomes aware of the observer, their behavior loses its naturalness and turns into “behavior performed for an audience.” The skill lies in erasing your own presence. It’s not about making the observer comfortable for the user — the observer must become part of the environment itself.&lt;/p&gt;
&lt;p&gt;Shadowing is the most direct tool for capturing a user’s unconscious behavior — not Activity, as discussed in the earlier post on behavior, but Behavior itself. However, since it can only follow one user deeply at a time, it suits gaining depth in an individual experience more than surveying patterns across a wide population.&lt;/p&gt;
&lt;h2 id=&quot;town-watching&quot;&gt;Town Watching&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“What patterns do people show overall in this place?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A method where you post up in a specific location and observe an unspecified number of users. If Shadowing is a tool for following one user deeply, Town Watching is a tool for broadly collecting behavioral patterns from many people in one place.&lt;/p&gt;
&lt;p&gt;You take a position in the target environment — a café, a store, a hospital waiting room, a subway station — and observe the flow of people. It’s hard to understand any individual user’s context deeply this way, but the method excels at surfacing recurring behavioral patterns: hesitation at a particular spot, movement that diverges from expectation, unconscious avoidance behavior.&lt;/p&gt;
&lt;p&gt;One thing to watch for is the observer’s own bias. It’s easy for the eye to be drawn only to “interesting” behavior, when it’s often the unremarkable, repetitive behavior that reveals a user’s unconscious cognition.&lt;/p&gt;
&lt;h2 id=&quot;behavioral-mapping&quot;&gt;Behavioral Mapping&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“What patterns emerge when you overlay users’ behavior onto a space?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A method of visualizing users’ movement or behavior on a two-dimensional plane for analysis. By recording the results of direct observation spatially, you can uncover patterned regularities that aren’t visible in any single instance of behavior.&lt;/p&gt;
&lt;p&gt;Overlay the paths of multiple users onto one map, and stalling, detours, and backtracking at specific points become visually apparent. The earlier post on behavior covered the case of a shopping mall’s elevator — Behavioral Mapping is useful for uncovering exactly that kind of problem. You can’t directly ask a user what they were thinking, but you can infer it from the pattern that many overlapping paths create.&lt;/p&gt;
&lt;p&gt;This tool is rarely used on its own; it’s more often used as a follow-up to organize and interpret data gathered through Shadowing or Town Watching.&lt;/p&gt;
&lt;h2 id=&quot;eye-tracking&quot;&gt;Eye Tracking&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“Where is the user’s gaze actually landing?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A method that tracks a user’s eye movements to record exactly where they’re looking on a screen or in a space. A user saying “I saw this button” and their gaze actually having landed on that button are two different things. Eye Tracking turns this gap into data.&lt;/p&gt;
&lt;p&gt;It’s one of the most direct tools for measuring unconscious cognition, but it comes with high equipment costs and constraints on the experimental environment. It also tells you “where” someone looked, but not “why.” Gaze data isn’t an answer in itself — it’s a clue that only gains meaning when interpreted alongside other observation tools.&lt;/p&gt;
&lt;h2 id=&quot;diary-study&quot;&gt;Diary Study&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“How does a user’s experience change over time?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A method that asks users to record their own experience over a set period. It’s useful for capturing situations an observer can’t always be present for — everyday context, long-term usage patterns, emotional shifts over time.&lt;/p&gt;
&lt;p&gt;This is the archetypal indirect-observation tool. Its strength is gathering experience from a user’s natural environment without an observer needing to see it firsthand. But keep in mind that the act of recording can itself distort the experience, and results can be skewed by the recorder’s own subjectivity. Since users only record what they’re consciously aware of, unconscious behavior remains this tool’s blind spot.&lt;/p&gt;
&lt;h2 id=&quot;interview&quot;&gt;Interview&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“How does the user themselves perceive their own experience?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A method of meeting a user face to face and asking directly about their experience. It’s the most familiar and accessible tool — and also the one that demands the most caution.&lt;/p&gt;
&lt;p&gt;Problems a user is consciously aware of — “this button is too small,” “loading is slow” — are relatively easy to surface through interviews. But as the earlier post on cognition showed, most cognition sits below the threshold of awareness, so users themselves often can’t accurately explain the substance of their own experience. When an answer comes back as “I don’t know why, but something about it feels off,” that’s not a limitation of the interview — it’s a feature of how cognition is structured. Answers can also be easily skewed by how the interviewer leads the questioning.&lt;/p&gt;
&lt;p&gt;Interviews are effective for confirming a user’s conscious awareness, but identifying the cause of unconscious behavior requires pairing them with other observation tools.&lt;/p&gt;
&lt;h2 id=&quot;survey&quot;&gt;Survey&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“What experience do users commonly, consciously recognize?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A method of collecting quantitative data from a large number of users through structured questions. If interviews are a tool for depth, surveys are a tool for breadth.&lt;/p&gt;
&lt;p&gt;Surveys are clearly cost-effective and can gather data at scale, but they inherit all the limitations of interviews and add a new one on top: bias in question design. The order of questions, the structure of the answer choices, the choice of wording — all of it can distort responses. What a survey can capture is limited to the range of experience a user can consciously recognize and put into words; the unconscious behavior beyond that range is out of reach.&lt;/p&gt;
&lt;h2 id=&quot;fgi-focus-group-interview&quot;&gt;FGI (Focus Group Interview)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“Does the conversation within a group surface experiences an individual wouldn’t consciously recognize alone?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A method where you recruit a representative group of target users to have a free-flowing conversation about their experience, guided by a moderator. It’s widely used in practice, and also one of the most rarely used correctly.&lt;/p&gt;
&lt;p&gt;The essence of an FGI is not “group interview” but “group conversation.” It’s often run as little more than a one-to-many interview with several people at once — that’s the wrong way to do it. The core of an FGI is that free conversation among participants stimulates each other’s memories and experiences, letting unconscious experiences that a person sitting alone would struggle to recall surface naturally within the flow of the conversation. The moderator’s job is to design and sustain that flow — and, just as importantly, to observe and collect participants’ unconscious reactions and behavior from a third-party vantage point: facial expressions, gestures, shifts in speech pace, avoidance of certain topics.&lt;/p&gt;
&lt;p&gt;A relaxed atmosphere is a precondition. The goal is to draw out stories buried in the unconscious by chatting casually with an experienced group about their experience — not to collect answers to a fixed set of questions.&lt;/p&gt;
&lt;h2 id=&quot;contextual-inquiry&quot;&gt;Contextual Inquiry&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“Can you trace the reasons behind a behavior together, in the very context where the user actually does their work?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A method of observing a user while simultaneously asking questions, in their actual working environment, while they carry out a task. It can be thought of as combining the on-the-ground quality of Shadowing with the explanatory power of an interview.&lt;/p&gt;
&lt;p&gt;The observer takes the posture of an “apprentice.” The user is the “master,” and the observer acts like someone there to learn the craft, naturally asking “why did you just do that?” This captures behavior while also confirming, on the spot, the context and reasoning behind it.&lt;/p&gt;
&lt;p&gt;Two things to watch for. First, questions must not interrupt the observation. If questions come frequently enough to break the flow of the work, the naturalness of the observation suffers. Second, you have to distinguish between a user’s real reason and post-hoc rationalization when they explain their own behavior. Users often answer “why?” not with the actual reason but with a plausible-sounding one they construct on the spot. This, too, is a feature of cognition covered in the earlier post on the subject.&lt;/p&gt;
&lt;h2 id=&quot;personal-inventories&quot;&gt;Personal Inventories&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“What do the objects a user carries reveal about their mental model?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A method of examining the personal items a group of users habitually carries in order to infer the mental model they’ve formed about a subject.&lt;/p&gt;
&lt;p&gt;Experience is an abstract image, and that abstractness is sometimes revealed more clearly through objects than through language. What items a user carries, and how they organize and use them, indirectly shows that user’s values, priorities, and way of perceiving the world. Because this tool doesn’t rely on a user’s verbal self-report, it can compensate for the blind spots of interviews and surveys.&lt;/p&gt;
&lt;h2 id=&quot;cultural-probes&quot;&gt;Cultural Probes&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“What cognitive clues are embedded in the everyday things users make?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A method of giving users tools — a camera, a diary, a map, postcards — and asking them to freely document their experience and environment over a set period. It resembles a Diary Study, but differs in that the medium of recording isn’t limited to language and extends into images and objects.&lt;/p&gt;
&lt;p&gt;The premise of this tool is that experience isn’t captured by language alone. The photos users take, the drawings they make, the objects they collect all become clues that reveal the user’s cognitive world in their own right. That said, these clues require interpretation, and the quality of the result depends heavily on the interpreter’s skill.&lt;/p&gt;
&lt;h2 id=&quot;zmet-zaltman-metaphor-elicitation-technique&quot;&gt;ZMET (Zaltman Metaphor Elicitation Technique)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;“When a user’s experience is expressed through images and metaphor, does it reveal cognition that language can’t reach?”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A method where you have users collect images (photos, pictures) related to an experience and talk through their own interpretation of those images, in order to extract the abstract cognitive structure underlying that experience.&lt;/p&gt;
&lt;p&gt;The core insight behind this approach is that experience is, by nature, an abstract image — and so the clues for inferring it can be gathered more richly through images and metaphor than through verbal statements. Ask a user “please describe your experience,” and you only pull up what’s floating on the surface of consciousness. Ask them “please choose an image that represents your experience,” and you open the possibility of drawing up the sensory, emotional dimensions that lie beneath the surface.&lt;/p&gt;
&lt;p&gt;That said, this is among the most abstract of the observation tools, and the process of interpreting the collected metaphors inevitably involves a heavy dose of the analyst’s own subjectivity. It’s better suited to gaining qualitative insight into a user’s cognitive world than to producing rigorous quantitative results.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Each of these tools has its own strengths and blind spots, and in real projects they’re rarely used alone — they’re more often woven together. You visualize a behavioral pattern found through Shadowing with Behavioral Mapping; you confirm, on the ground with Contextual Inquiry, a clue to experience that surfaced in an FGI; you explore, in an interview, an image obtained through ZMET, in greater depth.&lt;/p&gt;
&lt;p&gt;But whichever tool you use, and however skillfully you use it, one principle doesn’t change. The goal of these tools is to empathize with the cognition underlying a user’s surface behavior and representations. Fail to empathize — fail to synchronize with the user — and countless observations amount to nothing but the superficial. That costs a great deal and can end up accomplishing less than simply sitting and meditating on the user.&lt;/p&gt;
&lt;p&gt;No tool means anything if it fails to synchronize with the user.&lt;/p&gt;
&lt;p&gt;Having looked at the tools of observation, the next post turns to the tools of analysis — the tools for reading structure and meaning out of the data that observation accumulates.&lt;/p&gt;</content:encoded><category>The UX Design Manifesto</category><category>UX Design</category><category>User Research</category><category>Observation</category><category>UX Methodology</category></item><item><title>Wrapping Up the Series — Vibe Coding in a Nutshell</title><link>https://blog.bepuljang.co.kr/en/vibecoding/11/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/vibecoding/11/</guid><description>The close of the vibe coding series: the five principles running through it all, and a quick look back at every part.</description><pubDate>Mon, 13 Jul 2026 15:59:47 GMT</pubDate><content:encoded>&lt;p&gt;This is the last of the eleven-part series. So far, we’ve walked through vibe coding from the concepts to installing tools, building a first screen, sharpening prompts, deploying, refactoring, and hunting down bugs.&lt;/p&gt;
&lt;p&gt;In this post, we look back on that journey briefly and distill the parts worth remembering.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;five-principles&quot;&gt;Five Principles&lt;/h2&gt;
&lt;p&gt;Here’s the core that runs through the whole series, in five principles.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. One at a time&lt;/strong&gt;
Whether adding a feature or fixing a bug, ask for one thing at a time. Working in small units lets you find the cause fast when something goes wrong.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Provide context&lt;/strong&gt;
Give the AI your project structure and current state through &lt;code&gt;CLAUDE.md&lt;/code&gt;. An AI with context produces better results than one without.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Record right away&lt;/strong&gt;
If you plan to record later, you end up not doing it at all. Leave a line at the very moment you add a feature, and your history piles up naturally.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Tidy up regularly&lt;/strong&gt;
Only adding features and putting off cleanup lets complexity keep building. Set aside time to refactor every few weeks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. Working first&lt;/strong&gt;
Working code comes before perfect code. Once it works you can improve it; if it doesn’t work, there’s nothing to improve.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;a-look-back-at-the-series&quot;&gt;A Look Back at the Series&lt;/h2&gt;





















































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Part&lt;/th&gt;&lt;th&gt;Topic&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;What vibe coding is; a tool comparison&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;Installing Claude Code and your first conversation&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;Explaining your project to the AI with CLAUDE.md&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;Building your first screen; prompt basics&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;The structure-collapse trap and the habit of recording&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;Connecting external tools with MCP&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;Prompt engineering&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;First steps in deployment&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;9&lt;/td&gt;&lt;td&gt;Refactoring and tidying code&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;Debugging&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;11&lt;/td&gt;&lt;td&gt;Wrapping up (this post)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&quot;vibe-coding-is-a-means&quot;&gt;Vibe Coding Is a Means&lt;/h2&gt;
&lt;p&gt;The last thing worth emphasizing is that vibe coding isn’t an end in itself.&lt;/p&gt;
&lt;p&gt;Vibe coding is a means for turning ideas into reality quickly. Being able to build a service without coding knowledge is a genuine shift. But for what you build to last, you need more than raw speed — the habit of grasping your structure, the routine of leaving a record, and time set aside to tidy your code regularly.&lt;/p&gt;
&lt;p&gt;It’s fine if it’s not perfect, and fine if you don’t know everything from the start. Keep repeating the cycle of building, recording, and improving, and before you know it, your service will have grown. I hope this series helped, even in one small way, on that journey.&lt;/p&gt;</content:encoded><category>Vibe Coding for Everyone</category><category>vibe coding</category><category>development routine</category></item><item><title>On the Risk of Dominating Instead of Leading — Obedience Reaches Only as Far as the Order; Understanding Goes Beyond It</title><link>https://blog.bepuljang.co.kr/en/startup-prince/06/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/startup-prince/06/</guid><description>The second post in Part Two of The Startup Prince. Extracting obedience through the power of the office is not the same as leading people. We look at why an organization that runs on compelled obedience alone moves only while the founder is watching, borrowing Machiavelli&apos;s distinction between two kinds of power.</description><pubDate>Sun, 12 Jul 2026 12:46:02 GMT</pubDate><content:encoded>&lt;p&gt;The previous post was about choosing a battlefield you can win. Once you’ve chosen it, the next question is how to get people to move on it. And here a leader falls into one of the most common illusions there is: treating getting people to move and leading people as the same thing.&lt;/p&gt;
&lt;p&gt;The office of the founder comes with power. The authority to set salaries and reviews, to decide who is hired and fired, to make the final call. Use that power and getting people to move isn’t hard. Give an order and it usually gets done. The trouble is that what you’ve extracted that way is not leadership but obedience.&lt;/p&gt;
&lt;h2 id=&quot;the-organization-that-moves-only-while-the-founder-is-watching&quot;&gt;The Organization That Moves Only While the Founder Is Watching&lt;/h2&gt;
&lt;p&gt;An organization run by domination has clear symptoms. When the founder isn’t in the meeting, decisions stall. Even trivial matters travel upward on the words “we’ll have to ask the founder.” During the few days the founder is away, the organization defers its judgment until they return rather than starting anything new. On the surface the founder’s authority looks unshakable, but in reality the entire organization is confined inside the processing capacity of one person.&lt;/p&gt;
&lt;p&gt;An organization like this does the work it’s told to do well. It’s just that in the face of work it wasn’t told to do — the unexpected situation, the problem the founder never anticipated — it comes to a halt. Obedience only functions as far as the order reaches. What lies beyond it — moving in the right direction on your own when no one has told you to — is never produced by obedience.&lt;/p&gt;
&lt;h2 id=&quot;power-built-by-force-and-power-built-by-support&quot;&gt;Power Built by Force and Power Built by Support&lt;/h2&gt;
&lt;p&gt;In &lt;em&gt;The Prince&lt;/em&gt;, Machiavelli distinguishes two paths to power. One is to rise by borrowing the strength of the powerful few or through force; the other is to rise by winning the support of the people. He saw the stability of the two as sharply different. A ruler raised by a powerful few stands as their equal, is therefore always kept in check, and collapses when adversity strikes if the people’s goodwill isn’t there. A ruler standing on the people’s support, by contrast, he judged to have a solid foundation.&lt;/p&gt;
&lt;p&gt;The point is where the source of the power lies. Power built by force holds only while that force is still being applied. The moment the force stops — the moment the ruler steps away or their strength weakens — the power wavers with it. Power built on support, because people follow of their own accord, holds even when the ruler doesn’t compel them one by one. It’s the same logic as the fortune of the previous post: when the source of strength lies outside, it collapses when those conditions vanish; when it lies within, it lasts.&lt;/p&gt;
&lt;h2 id=&quot;obedience-reaches-only-as-far-as-the-order-understanding-goes-beyond-it&quot;&gt;Obedience Reaches Only as Far as the Order; Understanding Goes Beyond It&lt;/h2&gt;
&lt;p&gt;Translated to an organization, it goes like this. Obedience won through the power of the office functions only within the range that power reaches — as far as the founder’s eyes fall, the items the founder specified, the metrics that show up in reviews. The moment it crosses that boundary — a situation no one is watching, a problem not in the rulebook, a point where the instruction and reality diverge — obedience loses its force. Work that wasn’t ordered goes undone, and where judgment is called for, judgment gets deferred.&lt;/p&gt;
&lt;p&gt;Understanding works differently. If a member genuinely understands &lt;em&gt;what&lt;/em&gt; we are doing and &lt;em&gt;why&lt;/em&gt;, they move in the right direction even where there’s no instruction. Meeting an unforeseen problem, they reason out for themselves how the founder would have judged it and decide accordingly. This is what fills the beyond that obedience can’t reach. Machiavelli also said that ruling through fear can be the safer course — but fear only makes people pull back from a particular action; it doesn’t manufacture initiative where there was none. Fear is useful for preventing what shouldn’t be done, but making someone do a good thing no one ordered takes understanding.&lt;/p&gt;
&lt;h2 id=&quot;why-domination-is-easier&quot;&gt;Why Domination Is Easier&lt;/h2&gt;
&lt;p&gt;And yet many founders lean toward domination. The reason is simple: domination is faster and easier. Explaining why a task has to be done this way, sharing the criteria for judgment, and waiting until a member has absorbed it is slow and takes effort. Saying “just do it this way,” by contrast, produces an immediate result. In an early-stage startup where every day is urgent, that difference in speed is enormously tempting.&lt;/p&gt;
&lt;p&gt;The problem is that the bill for that convenience arrives later. An organization that has run on instruction alone grows larger while the number of people who can actually judge stays at one — the founder. If the organization has reached a hundred people but a hundred people’s worth of judgment all has to pass through a single person, that person is the bottleneck. The effort spared on planting understanding early comes back, once the organization has grown, as a ceiling on how far it can scale.&lt;/p&gt;
&lt;h2 id=&quot;the-work-of-planting-understanding&quot;&gt;The Work of Planting Understanding&lt;/h2&gt;
&lt;p&gt;Leading isn’t cutting back on instructions and leaving people to fend for themselves. It’s conveying context alongside the instruction instead of the instruction alone. When you pass along not just what to do but why it matters and by what criteria to judge it, a member gains the raw material to decide for themselves the next time a similar situation arises. Treating a decision made while the founder was away — even one whose outcome was disappointing — not as something to punish outright but as a chance to sharpen the criteria of judgment is the same work. Punish an act of independent judgment once, and the organization goes right back to waiting to be told.&lt;/p&gt;
&lt;p&gt;This isn’t the founder giving up power; it’s moving the source of that power from outside the office into the organization itself. An organization that moves only under compulsion stops the moment the founder stops compelling it, while an organization that moves through understanding moves even when the founder isn’t watching. To borrow Machiavelli’s terms, it is power standing on its own foundation rather than power standing on borrowed strength.&lt;/p&gt;
&lt;p&gt;The next post takes up the risk of that understanding flowing in one direction only. The founder has come as far as handing context to members — but still insists on making every judgment through their own eyes alone. It’s about how, the moment a leader overtrusts their own eye, the organization’s eyes fall shut.&lt;/p&gt;</content:encoded><category>The Startup Prince</category><category>Startup</category><category>Leadership</category><category>Organization</category><category>The Startup Prince</category></item><item><title>On the Risk of Fighting on a Battlefield You Cannot Win — Courage Charges In, Strategy Chooses the Ground</title><link>https://blog.bepuljang.co.kr/en/startup-prince/05/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/startup-prince/05/</guid><description>The first post in Part Two of The Startup Prince. Which battlefield you end up on is largely a matter of luck; how you fight once you&apos;re on it is a matter of skill. We look at the risk of grinding it out on a losing battlefield, borrowing the way Machiavelli saw the Romans wage war and the half of life he assigned to fortuna.</description><pubDate>Sat, 11 Jul 2026 14:39:50 GMT</pubDate><content:encoded>&lt;p&gt;Part One asked what a business stands on. Vision, money, contact points, fortune — things that look like foundations but can’t hold a business up for long. In Part Two the question changes. Once you’ve found ground to stand on, how do you lead the organization from there?&lt;/p&gt;
&lt;p&gt;The first of those questions concerns the earliest decision a leader makes: where to fight. In the previous post we looked at the risk of mistaking growth the market lifted for capability of your own. So where, exactly, do luck and capability each sit? That’s where this post begins.&lt;/p&gt;
&lt;h2 id=&quot;the-organization-that-grinds-it-out-on-unwinnable-ground&quot;&gt;The Organization That Grinds It Out on Unwinnable Ground&lt;/h2&gt;
&lt;p&gt;For a founder, grit is a virtue. The experience of forcing impossible things to work is what built today’s company, so most founders believe that the sheer volume of effort ultimately produces the result. But when that belief is aimed at the wrong target, the same virtue becomes what wears an organization down.&lt;/p&gt;
&lt;p&gt;There’s a familiar scene. A company charges head-on into a market where a dominant player has already entrenched itself, trying to close the gap with more marketing spend and longer hours. Or it pushes harder in a category where demand itself is structurally drying up, convinced the problem isn’t the market but its own execution. In these moments the organization is unmistakably working hard, and the founder has every reason to be proud of the effort. The trouble is that the effort is being spent on ground that was never designed to be won. This is the courage of the common man — brave enough to charge in, but never asking where.&lt;/p&gt;
&lt;h2 id=&quot;the-romans-did-not-postpone-their-wars&quot;&gt;The Romans Did Not Postpone Their Wars&lt;/h2&gt;
&lt;p&gt;In &lt;em&gt;The Prince&lt;/em&gt;, Machiavelli reports the Roman way like this: the Romans never postponed a war to avoid a conflict in front of them. They knew that a war is not avoided but only deferred, and that the side which benefits from the delay is always the other one. So the Romans chose to fight at a time and on ground favorable to themselves, before things could turn against them.&lt;/p&gt;
&lt;p&gt;The point here isn’t that the Romans were unafraid to fight. It’s that they chose when and where they would fight. Being brave and fighting anywhere are not the same thing. Roman strength lay not in the grit of never backing down, but in the judgment that pulled the conditions of the fight over to their own side.&lt;/p&gt;
&lt;h2 id=&quot;the-battlefield-is-set-by-luck-the-fight-is-waged-by-skill&quot;&gt;The Battlefield Is Set by Luck; the Fight Is Waged by Skill&lt;/h2&gt;
&lt;p&gt;There’s a saying: seven parts luck, three parts skill. Read as fatalism, it leaves you with nothing to do. But once you separate where the luck sits from where the skill sits, the same saying turns into practical guidance.&lt;/p&gt;
&lt;p&gt;Which battlefield you end up on — which market opens, whether that market’s structure favors a newcomer, whether the timing lines up — is largely a matter of luck. No amount of striving lets a founder conjure demand that isn’t there or overturn a market structure that has already set. How you fight once you’re on that battlefield, though — what you concentrate resources on, in what order you move, where you pull back — is a matter of skill. This is where capability actually does its work.&lt;/p&gt;
&lt;p&gt;Two postures split apart at this distinction. The courage of the common man pours skill into a losing battlefield. Believing that structurally unfavorable ground can be overturned by effort, it feeds its most precious resources — time and people — into a fight it cannot win. Cold strategy moves the other way. It first works out, without flinching, which battlefield is one that can be won, and then concentrates its skill there. The same skill vanishes without a trace on a losing battlefield and becomes the decisive difference on a winning one.&lt;/p&gt;
&lt;h2 id=&quot;how-to-recognize-a-battlefield-you-can-win&quot;&gt;How to Recognize a Battlefield You Can Win&lt;/h2&gt;
&lt;p&gt;So how do you tell a winnable battlefield from a losing one? A few questions help.&lt;/p&gt;
&lt;p&gt;First, is this market’s structure on your side or not? Does this market have examples of a latecomer overturning an incumbent, or is it one where the order, once set, rarely changes? In a market where network effects or switching costs already run strong, structure protects your rival no matter how well you play. Second, does the variable that decides the fight overlap with what you happen to be especially good at? If the ability that matters most for winning this market is precisely your strength, the ground is favorable; if your weak spot is where the fight is decided, it isn’t. Third, is the fight decided by the sheer volume of effort, or by structure? Some fights you win by working harder — but in others, structure has already fixed the outcome no matter how hard you work.&lt;/p&gt;
&lt;p&gt;Answer these honestly and it becomes clear whether the ground you’re standing on is a battlefield that effort can open, or one you should have chosen differently from the start. A sobering answer doesn’t mean you did something wrong; a large share of choosing the battlefield was luck to begin with. But the moment you avoid that answer, it becomes entirely the leader’s responsibility.&lt;/p&gt;
&lt;h2 id=&quot;a-retreat-is-not-a-defeat&quot;&gt;A Retreat Is Not a Defeat&lt;/h2&gt;
&lt;p&gt;None of this means you should avoid every hard fight. Every good battlefield looks difficult at first, and the easy ground usually already has a crowd on it. The point is to distinguish the kinds of difficulty. Difficulty you can clear through execution is not the same as difficulty manufactured by structure. The former is something to fight with skill; the latter is a signal to move the battlefield.&lt;/p&gt;
&lt;p&gt;That’s why strategy includes retreat and repositioning. In startups we usually call it a pivot. Pulling out of a losing battlefield to move onto one you can win comes not from a lack of grit but from knowing where to spend it. Just as the Romans chose a favorable moment to fight, a leader chooses ground that can be won and gathers the organization’s strength there. Courage charges in; strategy chooses.&lt;/p&gt;
&lt;p&gt;Once you’ve chosen the battlefield, the next question is how to get people to move on it. The next post takes up the difference between leading an organization and dominating it — why, when a leader extracts obedience through the power of the office, that organization moves only while the leader is watching.&lt;/p&gt;</content:encoded><category>The Startup Prince</category><category>Startup</category><category>Strategy</category><category>Market</category><category>The Startup Prince</category></item><item><title>Emotion Is the Temperature of the River — The Third Parameter of UX Design</title><link>https://blog.bepuljang.co.kr/en/uxd-manifesto/09/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/uxd-manifesto/09/</guid><description>This post addresses the layer of emotion that usability and conveniency alone cannot explain. Through Donald Norman&apos;s three levels of emotional response, we examine the role emotion plays in UX design and the traps that emotional design tends to fall into.</description><pubDate>Fri, 10 Jul 2026 15:04:56 GMT</pubDate><content:encoded>&lt;p&gt;We have looked at how users take in information and turn it into behavior through the three cognitive processes of selective attention, association, and mental models. But there is one thing that cannot be left out of how these cognitive procedures operate: emotion.&lt;/p&gt;
&lt;p&gt;Emotion is involved in every step of this process — as we select information, group it into meaning, and judge it against existing models. Emotion is not a byproduct of cognition; it is part of cognition itself.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Earlier in this series, emotion (Pleasurability) was introduced as the third UX design parameter, following usability and conveniency.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If usability and conveniency are relatively analytical and structural domains, emotion is the domain of affective value layered on top of them. And it is precisely this emotion that determines whether an experience gets “remembered” at all.&lt;/p&gt;
&lt;p&gt;An experience that was simply free of friction because the usability was good tends to be forgotten easily, while an experience that left an emotional impression stays with us for a long time. When we remember a product or service as “good,” that impression, more often than not, was formed at this level of emotion.&lt;/p&gt;
&lt;h2 id=&quot;the-layers-of-emotion&quot;&gt;The Layers of Emotion&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Where Emotion Comes From&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Emotional responses arise, broadly, at three levels. Donald Norman divided these into the Visceral, Behavioral, and Reflective levels — a distinction that is useful for showing that emotion is not simply a matter of “looking pretty.”&lt;/p&gt;
&lt;p&gt;The visceral level is the immediate, sensory reaction we have the moment we first encounter something. It is the first impression triggered by color, form, texture, sound, and it occurs before any conscious analysis takes place. The air and lighting the instant you step into a store, the feeling a screen gives you the moment you first open an app — these belong here. Emotion at this level is fast and powerful, but relatively short-lived.&lt;/p&gt;
&lt;p&gt;The behavioral level is the emotion that arises during use. It is the satisfaction that comes from the texture of interaction — the responsiveness of pressing a button, the feel of a door as it opens, the smoothness of a scroll. This level is closely tied to the usability and conveniency discussed earlier, and the simple sense that something “works well” translates directly into emotional satisfaction. In other words, even functional attributes can produce emotion.&lt;/p&gt;
&lt;p&gt;The reflective level is the emotion felt after use, or when looking back on the process of using something. It covers the self-image of “me, as someone who uses this product,” the desire to recommend it to others, and the trust and attachment one feels toward a brand. This level forms the most slowly of the three, but lasts the longest, and it dominates the final memory of the experience.&lt;/p&gt;
&lt;h2 id=&quot;emotion-cognition-and-behavior&quot;&gt;Emotion, Cognition, and Behavior&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;The Interplay Between Emotion and Cognition&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;What’s interesting is that emotion is not merely a result of cognition — it also shapes the direction cognition takes. In a positive emotional state, the scope of selective attention widens, association becomes more flexible, and the application of mental models grows more forgiving. In a state of anxiety or frustration, by contrast, attention narrows, minor inconsistencies provoke a sharper reaction, and even a small departure from an existing model produces confusion.&lt;/p&gt;
&lt;p&gt;This carries an important implication for UX design. If a negative emotion is triggered early in a use sequence, users become more likely to perceive everything that follows as “difficult,” no matter how well it is designed. Conversely, if a positive impression is established early on, minor inconveniences later in the sequence can be forgiven with far more generosity.&lt;/p&gt;
&lt;p&gt;Given that experience is formed through a sequence unfolding over time, the order and rhythm of emotion become a critical consideration in designing that sequence.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The Traps of Emotional Design&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;There are, however, traps to watch out for when dealing with emotion. The most common is the attempt to “add” emotion — building the function and structure first, then layering emotional elements on top afterward. This is no different from the early approach to design we examined earlier in this series’ look at design history, in which decoration was added on top of a craftsman’s output.&lt;/p&gt;
&lt;p&gt;Emotion is not something appended; it is something that permeates. It is already taking shape throughout every part of the process — the design of the use sequence, the structuring of information, the design of interactions. The tedium of a loading screen, the coldness of an error message, the emptiness felt after completing a task — all of these are emotional experiences. Emotional design means consciously managing these moments; it does not mean tacking a pretty illustration on at the end.&lt;/p&gt;
&lt;p&gt;The other trap is chasing a universal emotion. As emphasized earlier, emotional responses can differ completely from one user to another. A minimalist interface that feels “sophisticated” to one user may feel “unfriendly” to another. Emotion, too, must be designed on the basis of an understanding of the target user, and it is risky for designers to project their own emotional preferences onto the work.&lt;/p&gt;
&lt;p&gt;Borrowing again the river metaphor introduced earlier in this series, emotion is the temperature of the river’s water. Even if the water quality (cognition) is clean and the current (the sequence) flows smoothly, if the temperature isn’t right, no one wants to step into that river. And that temperature is not set at any single point along the river — it is formed across the entire course, from the very source down to the mouth.&lt;/p&gt;
&lt;p&gt;Emotion works the same way. Maintaining the user’s emotional temperature at the right level across the entire course of the experience — this is the role emotion plays in UX design.&lt;/p&gt;
&lt;p&gt;Starting with the next post, we turn to the concrete tools that put this declaration into practice, one at a time — beginning with the tools of observation: how to observe and capture the user’s cognition and emotion.&lt;/p&gt;</content:encoded><category>The UX Design Manifesto</category><category>UX Design</category><category>Emotional Design</category><category>Donald Norman</category><category>User Experience</category></item><item><title>When You Hit a Bug — Finding the Root Cause with AI</title><link>https://blog.bepuljang.co.kr/en/vibecoding/10/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/vibecoding/10/</guid><description>Learn how to find the root cause of a production bug together with AI and fix it.</description><pubDate>Fri, 10 Jul 2026 15:04:56 GMT</pubDate><content:encoded>&lt;p&gt;Once you deploy a service, you’re bound to run into bugs. Something that worked fine locally breaks after deployment, or a user emails you asking “why isn’t this working?” Let’s look at how to approach bugs when they show up in a service you built with vibe coding.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;where-production-bugs-come-from&quot;&gt;Where production bugs come from&lt;/h2&gt;
&lt;p&gt;Bugs are generally found through one of two paths.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Finding it yourself&lt;/strong&gt;
While using the service yourself, you notice that something isn’t behaving the way it should.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;User reports&lt;/strong&gt;
After you launch the service, a user tells you about a bug.&lt;/p&gt;
&lt;p&gt;Either way, the process for figuring out a bug is the same.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Can you reproduce it (under what conditions does it happen)&lt;/li&gt;
&lt;li&gt;Where does it happen (which code, which feature)&lt;/li&gt;
&lt;li&gt;Why does it happen (root cause)&lt;/li&gt;
&lt;li&gt;How do you fix it (resolution)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The hard part in vibe coding is step 3. Since you didn’t write all the code yourself, it’s not immediately obvious where the problem lies. This is where AI comes in.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;how-to-ask-ai-to-debug&quot;&gt;How to ask AI to debug&lt;/h2&gt;
&lt;p&gt;When asking AI to fix a bug, it’s important to give it as much information as possible.&lt;/p&gt;
&lt;h3 id=&quot;a-good-bug-report-format&quot;&gt;A good bug report format&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[Bug description]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;When I delete a bookmark it disappears from the list, but reappears when I refresh the page.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[Steps to reproduce]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;1. Add 3 bookmarks&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2. Click the delete button on the middle bookmark&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;3. It disappears from the list (expected)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;4. Refresh with F5&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;5. The deleted bookmark reappears (unexpected)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[Expected behavior]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;A deleted bookmark should stay gone even after a refresh.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[Browser console errors]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;(none / or paste the full error message)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[Files that seem related]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;BookmarkList.jsx, storage.js&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Giving this level of detail lets the AI immediately understand what it needs to look at.&lt;/p&gt;
&lt;h3 id=&quot;when-theres-an-error-message&quot;&gt;When there’s an error message&lt;/h3&gt;
&lt;p&gt;When a browser error occurs, a message shows up in red in the console (F12 → Console tab). Just copy the whole thing and paste it into Claude Code.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;I&apos;m getting the error below. Can you tell me what&apos;s causing it and how to fix it?&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Uncaught TypeError: Cannot read properties of undefined (reading &apos;filter&apos;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    at BookmarkList.jsx:23&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    at Array.map (&amp;#x3C;anonymous&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;    at BookmarkList (BookmarkList.jsx:18)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Error messages tell you which file and which line number the problem occurred on. The AI uses this information to find the relevant code and figure out the cause.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;prompt-patterns-that-come-in-handy-while-debugging&quot;&gt;Prompt patterns that come in handy while debugging&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Ask for a root-cause analysis first&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Analyze the root cause of this bug. Don&apos;t fix it yet — just explain why this is happening.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[Bug description]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Understand the root cause and figure out a fix direction before actually making changes. If you jump straight to “just fix it,” you sometimes end up patching the surface symptom while the underlying cause remains.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ask for step-by-step debugging&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Check through this step by step to find the cause of the bug.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Add console.log statements showing what values go in and out at each step.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;I&apos;ll run it myself and report back the results.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the AI can’t run the code itself, this is a way to narrow down the cause by having the developer run it and report the results back.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ask for verification after the fix&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Check whether the fix is heading in the right direction.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Also look at whether there&apos;s anything that could affect other features.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;where-else-does-this-touch--mapping-the-impact&quot;&gt;”Where else does this touch?” — Mapping the impact&lt;/h2&gt;
&lt;p&gt;Once you’ve found the root cause, you need to figure out how far the bug’s impact reaches. A single bug can affect multiple features at once.&lt;/p&gt;
&lt;p&gt;For example, if there’s a problem with how data is saved to localStorage, it could affect not just bookmark storage but every feature that uses localStorage — tag storage, settings storage, and so on.&lt;/p&gt;
&lt;p&gt;Here too, you can just ask the AI. It’s more accurate when the structure is laid out in &lt;code&gt;CLAUDE.md&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Check which components are connected to storage.js.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;If there&apos;s a bug in the localStorage save logic, figure out which features would be affected.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Claude Code reads the code and structure notes and works out the scope of impact. The better the structure is recorded, the more accurate the analysis will be.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;things-to-check-after-fixing-a-bug&quot;&gt;Things to check after fixing a bug&lt;/h2&gt;
&lt;p&gt;After fixing a bug, there are two things to check.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Is the bug actually fixed&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Walk through the reproduction steps again to confirm the bug is gone. Check locally first, then deploy and confirm in the real environment too.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Did anything else break&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A bug fix can affect other features. Use the features within the impact scope you identified earlier and confirm nothing is off.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;habits-that-prevent-bugs&quot;&gt;Habits that prevent bugs&lt;/h2&gt;
&lt;p&gt;It’s more efficient to prevent bugs from happening in the first place than to fix them after the fact.&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Habit&lt;/th&gt;&lt;th&gt;Method&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Test it yourself after adding a feature&lt;/td&gt;&lt;td&gt;Whenever you add a new feature, use the related features yourself to check&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Prompts with explicit scope&lt;/td&gt;&lt;td&gt;When asking AI for something, explicitly state that it shouldn’t affect existing features&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Deploy in small increments&lt;/td&gt;&lt;td&gt;Deploy little by little instead of shipping many features at once&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Keep a change log&lt;/td&gt;&lt;td&gt;Record what changed and when (GitHub commit messages)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;You can’t prevent bugs perfectly in vibe coding. But it’s important to have a setup where you can quickly find the cause and fix it once one shows up.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;next-time&quot;&gt;Next time&lt;/h2&gt;
&lt;p&gt;In the final installment, we’ll cover how to keep a service you built with vibe coding running for the long term — the routines and mindset that keep it manageable as it grows, without losing the speed you had at the start.&lt;/p&gt;</content:encoded><category>Vibe Coding for Everyone</category><category>Debugging</category><category>Bugs</category><category>Claude Code</category><category>Vibe Coding</category></item><item><title>Context — a Path to Hand Values to Distant Components</title><link>https://blog.bepuljang.co.kr/en/react/08/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/react/08/</guid><description>How Context solves the fatigue of drilling Props down step by step. The three stages of createContext, Provider, and useContext — and when to use it versus when to hold back.</description><pubDate>Fri, 10 Jul 2026 07:12:44 GMT</pubDate><content:encoded>&lt;p&gt;Up to last time we looked at the values a single component handles on its own (State and Ref). Now let’s widen the view to &lt;strong&gt;several components sharing a value&lt;/strong&gt;. We said Props hand a value from parent to child — but there are moments when that isn’t enough. What fills that gap is &lt;strong&gt;Context&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;prop-drilling--the-fatigue-of-carrying-a-value-down-like-stairs&quot;&gt;Prop drilling — the fatigue of carrying a value down like stairs&lt;/h2&gt;
&lt;p&gt;Props are passed &lt;strong&gt;one step at a time&lt;/strong&gt;. Parent → child → grandchild, one level at a time. But what if a value set at the top (say, a dark/light theme) has to be used by a button five levels down?&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;App&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; theme&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{theme}&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Layout&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; theme&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{theme}&gt;       {&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/* Layout doesn&apos;t even use theme… */&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Sidebar&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; theme&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{theme}&gt;    {&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/* Sidebar just passes it along… */&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Button&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; theme&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{theme} /&gt; {&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/* the one that truly needs it is here */&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The intermediate &lt;code&gt;Layout&lt;/code&gt; and &lt;code&gt;Sidebar&lt;/code&gt; have to keep receiving and passing down &lt;code&gt;theme&lt;/code&gt; &lt;strong&gt;without even using it&lt;/strong&gt;, just to get it further down. Carrying a value tier by tier like this is called &lt;strong&gt;prop drilling&lt;/strong&gt;. The deeper the layers, the messier the code, and the harder it is to tell what each middle component is even carrying.&lt;/p&gt;
&lt;p&gt;Context lets you skip these stairs. Broadcast a value &lt;strong&gt;once&lt;/strong&gt; at the top, and a component at any depth below can &lt;strong&gt;pull it directly&lt;/strong&gt;, without going through the middle.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;the-three-stages-of-context&quot;&gt;The three stages of Context&lt;/h2&gt;
&lt;p&gt;You use Context in exactly three stages.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;① Create it — &lt;code&gt;createContext&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { createContext } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;react&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;export&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; ThemeContext&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; createContext&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;light&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;); &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// default value&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What &lt;code&gt;createContext&lt;/code&gt; returns is not a component but a single &lt;strong&gt;“conduit object.”&lt;/strong&gt; This object links the side that broadcasts a value to the side that pulls it out.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;② Broadcast it — wrap with &lt;code&gt;&amp;#x3C;Context&gt;&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; MyPage&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;ThemeContext&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; value&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;dark&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Form&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; /&gt;   {&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/* anything at any depth in here can pull &apos;dark&apos; */&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;ThemeContext&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside the region wrapped by &lt;code&gt;ThemeContext&lt;/code&gt;, that value can be subscribed to regardless of level.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Older code uses the form &lt;code&gt;&amp;#x3C;ThemeContext.Provider value=&quot;dark&quot;&gt;&lt;/code&gt;. Since React 19 it’s been shortened so you can write &lt;code&gt;&amp;#x3C;ThemeContext value=...&gt;&lt;/code&gt; directly, as above. They’re the same thing.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;③ Pull it out — &lt;code&gt;useContext&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { useContext } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;react&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Button&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; theme&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useContext&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(ThemeContext); &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// pulled directly, no middle steps&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; className&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{theme}&gt;Button&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No matter how deep &lt;code&gt;Button&lt;/code&gt; sits, one line of &lt;code&gt;useContext(ThemeContext)&lt;/code&gt; receives the broadcast value directly. All those &lt;code&gt;theme&lt;/code&gt; props climbing up and down the stairs disappear.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;not-just-constants--state-and-functions-too&quot;&gt;Not just constants — state and functions too&lt;/h2&gt;
&lt;p&gt;The value you broadcast needn’t be a fixed constant. &lt;strong&gt;Combine it with State&lt;/strong&gt; and you can even change the value from below.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; MyPage&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;theme&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;setTheme&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useState&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;dark&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;ThemeContext&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; value&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{{ theme, setTheme }}&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Form&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;ThemeContext&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Send &lt;code&gt;theme&lt;/code&gt; and &lt;code&gt;setTheme&lt;/code&gt; down together, and a lower component can not only &lt;strong&gt;read&lt;/strong&gt; the value but &lt;strong&gt;change&lt;/strong&gt; it. A classic example is an auth context that broadcasts the logged-in user info along with a &lt;code&gt;login()&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;Providers can also be nested, and in that case &lt;strong&gt;the nearest one wins.&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;ThemeContext&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; value&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;dark&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;ThemeContext&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; value&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;light&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Footer&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; /&gt;   {&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/* subscribes to the nearest &apos;light&apos; */&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;ThemeContext&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;ThemeContext&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;when-to-use-it-when-to-hold-back&quot;&gt;When to use it, when to hold back&lt;/h2&gt;
&lt;p&gt;Context is powerful but &lt;strong&gt;not a universal tool.&lt;/strong&gt; It shines for broadcasting values the &lt;strong&gt;whole app uses broadly&lt;/strong&gt; — theme, the logged-in user, language settings. Conversely, if you pull every value that only needs to go two or three levels into Context, the origin of the value gets blurry and harder to trace. Think of it as the tool you reach for &lt;strong&gt;when prop drilling actually becomes a pain.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;One more thing. When a Context value changes, the lower components subscribing to it &lt;strong&gt;re-render&lt;/strong&gt;. The wider the region a Context wraps, the larger the ripple of these re-renders can be, and there comes a point where you have to mind performance. How to cut down on “more re-renders than necessary” is exactly the subject of the next article. Finally, we’ll look at &lt;strong&gt;rendering optimization&lt;/strong&gt; — &lt;code&gt;useMemo&lt;/code&gt;, &lt;code&gt;useCallback&lt;/code&gt;, and &lt;code&gt;memo&lt;/code&gt;.&lt;/p&gt;</content:encoded><category>React — Web Development from Start to Deploy</category><category>React</category><category>Context</category><category>useContext</category><category>Shared State</category></item><item><title>On the Risk of Scaling by Fortune Alone — Luck Opens the Door, Skill Holds the Room</title><link>https://blog.bepuljang.co.kr/en/startup-prince/04/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/startup-prince/04/</guid><description>The final post in Part One of The Startup Prince. We examine the risk of mistaking growth carried by market tailwinds for one&apos;s own capability, borrowing Machiavelli&apos;s distinction between virtù and fortuna.</description><pubDate>Fri, 10 Jul 2026 07:12:44 GMT</pubDate><content:encoded>&lt;p&gt;So far we’ve looked at three false foundations: vision, money, and contact points. The last risk to close out Part One is, of all of them, the hardest to notice. It’s growth that’s happening not because you’re doing well, but because you’re lucky.&lt;/p&gt;
&lt;h2 id=&quot;why-a-thriving-company-collapses-the-moment-the-market-turns&quot;&gt;Why a Thriving Company Collapses the Moment the Market Turns&lt;/h2&gt;
&lt;p&gt;It’s a familiar scene in hindsight. An entire industry catches an unusually strong tailwind at a particular moment. During the pandemic, when demand for remote services exploded, or during a period of cheap money when capital was everywhere, a whole cohort of startups drew the same steep growth curve at once. The founders inside those companies find it easy to believe their own execution and strategy produced that growth.&lt;/p&gt;
&lt;p&gt;But when the wind changes, you watch several companies that rose together at the same moment collapse together too. What surfaces only then is that a large share of that growth was actually the market lifting everyone at once — and underneath it, the organization, the cost structure, the decision-making systems were never made sturdy enough to carry that growth on their own.&lt;/p&gt;
&lt;p&gt;This moment repeats across every industry. When demand in a particular category spikes temporarily, a cohort of companies that caught that wave early grow at roughly the same pace. The founders inside them, watching each other’s growth, become convinced their own strategy has been validated — when in fact they’re just several boats riding the same swell. Only once the swell flattens out does the real difference between the boats — how sound each hull actually is — become visible.&lt;/p&gt;
&lt;h2 id=&quot;virtù-and-fortuna&quot;&gt;Virtù and Fortuna&lt;/h2&gt;
&lt;p&gt;In &lt;em&gt;The Prince&lt;/em&gt;, Machiavelli distinguishes two paths to power: one gained through a ruler’s own capability — &lt;em&gt;virtù&lt;/em&gt; — and one gained through sheer luck — &lt;em&gt;fortuna&lt;/em&gt;. He explains why power gained through fortune is hard to hold onto: the conditions that granted it — a friendly faction, a chance turn of events, good timing — were never something the ruler created himself, so when those conditions vanish, so does any means of defending the power they gave him. Power built through one’s own capability, by contrast, has its source inside the ruler, so it survives even as conditions change.&lt;/p&gt;
&lt;p&gt;Machiavelli adds an interesting detail here: among those who gained power through fortune, the ones who used the time that fortune bought them to actually build their own capability were the ones who survived what came after. Fortune only opens the door. Holding the room once you’re inside is entirely a matter of capability.&lt;/p&gt;
&lt;h2 id=&quot;what-didnt-get-done-during-the-good-years&quot;&gt;What Didn’t Get Done During the Good Years&lt;/h2&gt;
&lt;p&gt;Translated to startups, the real danger isn’t riding a market tailwind. It’s failing to do the preparation that tailwind gave you time for. When revenue sets a new record every month, nobody feels the urge to examine the cost structure. When hiring is easy, gaps in organizational process rarely surface. When new users are pouring in, the topline numbers keep looking good enough that nobody has to look closely at retention.&lt;/p&gt;
&lt;p&gt;All of it surfaces at once the moment the tailwind stops. A cost structure sized for that era’s revenue can’t be sustained once revenue turns down. An organization that grew at that era’s hiring pace turns out to have processes that were never built to carry it. Weak retention only becomes unmistakable once new-user growth dries up and there’s nothing left to mask it. In the real world, a wave of startups that grew fast on cheap capital during a low-rate period shrank or shut down at almost the same time once rates rose and investors turned cautious. They didn’t suddenly become incompetent. What was true all along — that they’d grown through luck rather than capability — simply became visible the moment the luck lifted.&lt;/p&gt;
&lt;h2 id=&quot;a-question-that-separates-skill-from-luck&quot;&gt;A Question That Separates Skill From Luck&lt;/h2&gt;
&lt;p&gt;The trouble is that in the middle of a growth spurt, it’s genuinely hard to tell whether it’s coming from capability or from luck. Here’s one question that helps: did competitors who started around the same time under similar conditions grow too, or did we grow unusually fast on our own? If the whole industry is drawing the same curve, then a large share of that growth was made by the market, and what your own capability actually contributed is closer to the &lt;em&gt;difference&lt;/em&gt; — how much faster, how much more healthily you grew than the competitors riding the same wave.&lt;/p&gt;
&lt;p&gt;An organization that makes this distinction honestly knows exactly how much of its position it can actually defend once the market turns. An organization that has credited all of its growth to its own capability only discovers, the moment the market turns, just how little of it was ever really theirs to defend.&lt;/p&gt;
&lt;h2 id=&quot;what-to-do-while-youre-lucky&quot;&gt;What to Do While You’re Lucky&lt;/h2&gt;
&lt;p&gt;The conclusion isn’t to refuse good fortune. There’s no reason to turn luck away. It’s that the moment you’re lucky is exactly the moment to build capability. Could you sustain your cost structure at a revenue level smaller than today’s? Do you have a cash buffer that survives a slowdown in growth? As hiring speeds up, are the processes people actually need to do the work keeping pace with it? Building answers to these questions while things are going well is the only real way to hold your seat once luck moves on.&lt;/p&gt;
&lt;p&gt;This isn’t a check you run once. An organization that keeps asking the same questions every quarter — could we survive on a smaller cost structure, do we have cash to survive six months without growth — ends up in a completely different position than one that assumes growth will simply continue and keeps deferring those questions, the moment the market turns.&lt;/p&gt;
&lt;p&gt;We’ll return to this in the series’ final post, but decline usually doesn’t arrive the instant luck disappears. It arrives later, as the bill for what wasn’t prepared during the good years finally comes due.&lt;/p&gt;
&lt;p&gt;Part One is now complete, having looked at four false foundations: vision, money, contact points, and fortune. Starting with the next post, we move into Part Two — the problem of leadership, and how to lead an organization once it has found its footing.&lt;/p&gt;</content:encoded><category>The Startup Prince</category><category>Startup</category><category>Market Timing</category><category>Scaling</category><category>The Startup Prince</category></item><item><title>Behavior Is the Whirlpool of Cognition — How to Observe the Invisible</title><link>https://blog.bepuljang.co.kr/en/uxd-manifesto/08/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/uxd-manifesto/08/</guid><description>A user&apos;s cognition can never be observed directly. But wherever cognition goes astray, the user is bound to show behavior that differs from the norm. Through the shopping mall elevator and convenience store milk examples, we look at why observing behavior is the starting point of UX design.</description><pubDate>Fri, 10 Jul 2026 07:12:44 GMT</pubDate><content:encoded>&lt;p&gt;When water flows smoothly and then suddenly swirls at some point, there must be a landform beneath it obstructing the flow — something that makes the surface and the depths move differently, revealing itself in the shape of a whirlpool. On the surface, all we see is water spinning in circles, but the cause always lies somewhere beneath the surface.&lt;/p&gt;
&lt;p&gt;To borrow again the metaphor this series has used to compare user experience to water: this whirlpool is precisely the unusual behavior a user displays. When there is a point in the flow of experience that is not smooth, the user — without realizing it — exhibits behavior different from usual, and that behavior becomes the surest signal that a problem exists.&lt;/p&gt;
&lt;p&gt;It is difficult to look directly beneath the water, but finding a whirlpool is easy. Likewise, it is difficult to look directly into a user’s cognition, but observing behavior is comparatively easy. This is precisely why the observation of behavior occupies such a central place in user experience design.&lt;/p&gt;
&lt;h2 id=&quot;what-is-behavior--how-the-unconscious-reveals-itself&quot;&gt;What Is Behavior? — How the Unconscious Reveals Itself&lt;/h2&gt;
&lt;p&gt;So what exactly do we mean by “behavior” here? Behavior refers to the movements we make unconsciously. Unlike activity — something we consciously choose and carry out — behavior is closer to a motion or response we take naturally, without being aware of it ourselves.&lt;/p&gt;
&lt;p&gt;As we saw earlier, unconscious cognition shapes experience far more than the conscious cognition that appears on the surface, because most of the countless judgments and choices we make in daily life occur beneath the surface of awareness. This is why observing unconscious behavior carries far greater significance for understanding user experience than observing conscious activity.&lt;/p&gt;
&lt;p&gt;The importance of observing behavior is not limited to this matter of relative weight. Problems that users consciously feel — “this button is too small,” “the loading is slow” — are relatively easy to report through surveys or interviews.&lt;/p&gt;
&lt;p&gt;But problems that arise in the realm of the unconscious go unrecognized by users themselves, so even when asked directly, they rarely surface in answers. And because they go unanswered, they are naturally never recognized as problems, and therefore never solved. The moment a user feels, “I don’t know why, but something about this is uncomfortable,” is exactly when such an unconscious cognitive problem is at work — and observing behavior is nearly the only route by which we can capture it.&lt;/p&gt;
&lt;h2 id=&quot;the-mall-elevator-and-the-meaning-of-a-vanished-path&quot;&gt;The Mall Elevator and the Meaning of a Vanished Path&lt;/h2&gt;
&lt;p&gt;Let’s look at a concrete example. Suppose a shopping mall has just opened, and oddly enough, when its visitor traffic is analyzed, one particular spot repeatedly shows people circling or pausing hesitantly.&lt;/p&gt;
&lt;p&gt;At first, one might simply assume there’s an interesting store in that area drawing people in. But suppose a closer analysis of what this pattern means reveals something else: people looking for the elevator keep heading toward that spot, failing to find it, and turning back.&lt;/p&gt;
&lt;p&gt;What does this repeated behavior mean? For some reason, users expected an elevator to be there, and when that expectation was not met, they experienced confusion.&lt;/p&gt;
&lt;p&gt;We use a variety of cues — memory, location, signage, atmosphere, color — to grasp the character of a given space and time and act accordingly. The causes here, too, could be varied. Perhaps in most other malls, an elevator is typically located in that kind of position, so users, drawing on past experience, assumed this mall would follow the same pattern.&lt;/p&gt;
&lt;p&gt;Or perhaps the atmosphere created by a water cooler and a sofa placed near the corridor gave the impression that an elevator must lie further inside. Or perhaps certain kinds of brands that are commonly found near elevators happened to have stores in that area, leading users to unconsciously assume, “since this brand is here, the elevator must be nearby too.”&lt;/p&gt;
&lt;p&gt;What’s worth noting is that none of these judgments are the product of conscious thought. We do not simply read directional signage and move accordingly; rather, we gather a range of information — the shape of the space, the arrangement of objects, the atmosphere, memory — and piece it together through inference, reducing cognitive load in the process. Most of the time, these inferences turn out to be correct, but when the environmental cues underlying them diverge from reality, errors in behavior can result.&lt;/p&gt;
&lt;h2 id=&quot;the-solution-is-the-environment-not-a-sign&quot;&gt;The Solution Is the Environment, Not a Sign&lt;/h2&gt;
&lt;p&gt;So how can this problem be solved? In a building that has already been constructed, changing the actual location of the elevator is not realistically feasible. But it is possible to reconfigure the environmental elements that shape users’ unconscious judgments, so that the spot in question no longer reads as “elevator territory.”&lt;/p&gt;
&lt;p&gt;Depending on the cause, a range of solutions exist: changing the shape of the corridor, rearranging the objects that cause the misunderstanding, shifting the atmosphere of the space, guiding the flow of foot traffic naturally toward the real elevator, or repositioning stores.&lt;/p&gt;
&lt;p&gt;If all these approaches fail, a large sign or standing signboard ends up going in — but this should be the very last resort. A sign does not resolve the problem at its root; it simply places an additional conscious cognitive burden on the user, which is why, from the standpoint of user experience design, it is the least desirable solution.&lt;/p&gt;
&lt;h2 id=&quot;heuristics-and-how-we-find-milk-at-the-convenience-store&quot;&gt;Heuristics, and How We Find Milk at the Convenience Store&lt;/h2&gt;
&lt;p&gt;In the mall example above, we saw users unconsciously making judgments by piecing together a variety of cues. This mode of cognitive processing through inference has a name.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is called a heuristic. It is a cognitive device that helps us make fast decisions by drastically reducing the amount of information we process, rather than carefully reviewing everything.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In fact, in most cases, this heuristic functions as an essential cognitive shortcut. If we had to consciously process every piece of information at every moment, even the simplest everyday actions would consume an enormous amount of cognitive energy.&lt;/p&gt;
&lt;p&gt;For example, the reason we can find milk relatively easily even in a convenience store we’re visiting for the first time is not because we were specifically told where the milk is. Rather, our feet move unconsciously, guided by a kind of mental model built from past memory and experiential judgment — the sense that milk is usually displayed in a certain spot within the refrigerated section, amid a certain kind of atmosphere.&lt;/p&gt;
&lt;p&gt;In this way, we repeat this kind of interaction countless times, not only in physical space but also across the many user interfaces we encounter in digital environments. When the connection between this automatic behavior and the expected outcome holds up smoothly, the experience flows without friction; but the moment that connection breaks, the need for conscious review and correction arises.&lt;/p&gt;
&lt;h2 id=&quot;breaking-the-flow-is-also-design--intentional-friction&quot;&gt;Breaking the Flow Is Also Design — Intentional Friction&lt;/h2&gt;
&lt;p&gt;What’s interesting is that even this chasm between automatic flow and conscious review can, at times, become a deliberate element of user experience design.&lt;/p&gt;
&lt;p&gt;If a piece of important information demands a user’s full attention — if it requires conscious review rather than simply being carried along by the unconscious flow of behavior — then deliberately disrupting that flow can actually heighten the user’s attention. Requiring a deliberate scroll before agreeing to important terms in a financial service, or adding a confirmation step before an irreversible action, are examples of this.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This approach, however, should be used only in rare and carefully considered cases. In most situations, user experience design aims to guide the natural flow of experience and behavior forward, not interrupt it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So when this kind of gap occurs unintentionally, it should be treated as one of those rock formations beneath the water that impede user experience. To return to the metaphor from the beginning: it is a whirlpool occurring where it is not wanted.&lt;/p&gt;
&lt;h2 id=&quot;behavior-is-the-only-window-into-cognition&quot;&gt;Behavior Is the Only Window Into Cognition&lt;/h2&gt;
&lt;p&gt;Let’s bring the discussion so far together. In the previous post, we established that user experience design is, at its core, “design for user cognition.” But as has been said repeatedly, cognition is not something we can observe or correct directly.&lt;/p&gt;
&lt;p&gt;Because cognition is an internal process that takes place inside the user’s mind, we have no choice but to infer it by observing the “behavior” through which users outwardly express their cognition of things. Just as we identified a problem in spatial cognition through the circling paths of people in the mall, behavior is precisely the only window through which we can access cognition.&lt;/p&gt;
&lt;p&gt;Likewise, when we correct something through user experience design, users come to perceive that corrected thing anew, and that new perception is, again, expressed through behavior. It is also only through this behavior that a designer can infer whether a design is working as intended, whether the user experience has actually improved. In other words, the entire process of user experience design both begins and ends with behavior.&lt;/p&gt;
&lt;p&gt;For exactly this reason, the “UX design methods” this series will cover going forward place heavy emphasis on observing behavior and analyzing it. Designing an experience, or observing behavior to infer cognition, may be understandable as a concept, but the moment you try to actually put it into practice, it’s easy to feel lost about where and how to even begin.&lt;/p&gt;
&lt;p&gt;Which behaviors should be observed, what cognitive meaning should be read from them, and how that interpretation should be translated into design improvements — all of this requires a concrete methodology. UX design methods are precisely what offer a systematic procedure and framework of thought at this point, helping us move step by step toward the ultimate goal of user experience design: the correction of user cognition.&lt;/p&gt;
&lt;p&gt;So far, we have looked at what UX design is, by what principle experience is formed, and what role cognition and behavior each play within it. The core of the discussion has been this: cognition lies at the root of experience, that cognition can only be observed through the window of behavior, and the results of design are likewise verified through behavior.&lt;/p&gt;
&lt;p&gt;The next post will take up emotion — another axis, alongside cognition and behavior, that constitutes experience. Having long been addressed in traditional design, it may feel familiar, but it remains a powerful and distinct UX design parameter in its own right.&lt;/p&gt;</content:encoded><category>The UX Design Manifesto</category><category>UX Design</category><category>User Behavior</category><category>Heuristics</category><category>Cognition</category></item><item><title>Cleaning Up Your Code After Launch — How to Refactor with AI</title><link>https://blog.bepuljang.co.kr/en/vibecoding/09/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/vibecoding/09/</guid><description>Learn how to handle the refactoring that every vibe-coded project eventually needs, working alongside AI.</description><pubDate>Fri, 10 Jul 2026 07:12:44 GMT</pubDate><content:encoded>&lt;p&gt;Once you launch your service, you hit a fork in the road: keep adding new features, or take a moment to clean up the code you’ve built so far.&lt;/p&gt;
&lt;p&gt;For a vibe coding project where you’ve been building fast, this moment matters. If you keep piling on features without ever cleaning up, eventually even the AI will have trouble making sense of your codebase. In this post, we’ll cover how to read and clean up your code together with AI — in other words, &lt;strong&gt;refactoring&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;why-cleanup-is-essential-after-vibe-coding&quot;&gt;Why cleanup is essential after vibe coding&lt;/h2&gt;
&lt;p&gt;Refactoring is &lt;strong&gt;the work of improving your code’s structure without changing its behavior&lt;/strong&gt;. There’s no visible change to the user, but it makes future edits and extensions much easier.&lt;/p&gt;
&lt;p&gt;Code that’s been built quickly through vibe coding tends to develop a common set of problems.&lt;/p&gt;






























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Problem&lt;/th&gt;&lt;th&gt;Example&lt;/th&gt;&lt;th&gt;Impact&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;One file doing too many jobs&lt;/td&gt;&lt;td&gt;&lt;code&gt;App.jsx&lt;/code&gt; handles UI + logic + data processing all at once&lt;/td&gt;&lt;td&gt;Hard to figure out where to make a fix&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Duplicate code&lt;/td&gt;&lt;td&gt;Similar functions exist in 2-3 different places&lt;/td&gt;&lt;td&gt;Fixing one means you have to manually update the others&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Inconsistent naming&lt;/td&gt;&lt;td&gt;&lt;code&gt;getBookmarks&lt;/code&gt;, &lt;code&gt;fetchData&lt;/code&gt;, &lt;code&gt;loadItems&lt;/code&gt; all do the same thing&lt;/td&gt;&lt;td&gt;Takes longer to read and understand the code&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Hardcoded values&lt;/td&gt;&lt;td&gt;Strings like &lt;code&gt;localStorage.getItem(&apos;bm-data-v1&apos;)&lt;/code&gt; scattered in multiple places&lt;/td&gt;&lt;td&gt;Renaming a key means hunting down every occurrence&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;None of these problems are noticeable while you’re quickly adding features, but as the code piles up, adding new features or fixing bugs starts taking longer and longer.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;how-to-read-and-improve-your-codes-structure-with-ai&quot;&gt;How to read and improve your code’s structure with AI&lt;/h2&gt;
&lt;p&gt;To refactor on your own, you need to understand the code first. But since your vibe-coded code was written by AI, it can be hard to know exactly what lives where.&lt;/p&gt;
&lt;p&gt;This is another place where you lean on AI. Here’s a good prompting approach for refactoring requests.&lt;/p&gt;
&lt;h3 id=&quot;step-1-ask-for-an-analysis-of-the-current-code&quot;&gt;Step 1: Ask for an analysis of the current code&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Read through the current bookmark-app project&apos;s code and tell me:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;1. What role each file plays&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;2. Where any single file is doing too many jobs&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;3. Where there&apos;s duplicated logic&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;4. The top 3 things worth improving&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Don&apos;t make any changes yet — just analyze.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Asking for analysis first matters. If you jump straight to “fix it,” the AI might change far too much at once.&lt;/p&gt;
&lt;h3 id=&quot;step-2-set-priorities&quot;&gt;Step 2: Set priorities&lt;/h3&gt;
&lt;p&gt;Once the AI shares its analysis, decide what order to tackle things in. Don’t try to fix everything at once.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;From the analysis above, let&apos;s start with item 3 (separating out the localStorage logic).&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Move the localStorage-related code currently in App.jsx into a storage.js file.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Keep the functionality exactly the same — just change the structure.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;step-3-verify-it-still-works-after-the-change&quot;&gt;Step 3: Verify it still works after the change&lt;/h3&gt;
&lt;p&gt;After changing the code, always check in the browser that the feature still works the same way. Refactoring is supposed to leave functionality untouched, but mistakes can still happen.&lt;/p&gt;
&lt;h3 id=&quot;step-4-move-on-to-the-next-item&quot;&gt;Step 4: Move on to the next item&lt;/h3&gt;
&lt;p&gt;Once you’ve verified it, move on to the next item. One thing at a time.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;prompt-patterns-youll-use-often-when-refactoring&quot;&gt;Prompt patterns you’ll use often when refactoring&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Splitting out a component&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;In App.jsx, split out the part that displays the bookmark list into a BookmarkList.jsx component.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Also handle wiring up the props and callback functions.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Extracting shared logic&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;The functions fetchBookmarks(), saveBookmark(), and deleteBookmark() are scattered across different files.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Combine them into a custom hook at hooks/useBookmarks.js.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Cleaning up constants&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Collect the values hardcoded as strings in the code (like localStorage key names)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;into a constants.js file.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Removing unnecessary code&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Clean up commented-out code, unused imports, and unfinished functions.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Only remove things that won&apos;t affect functionality.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;reflecting-the-refactoring-results-in-claudemd&quot;&gt;Reflecting the refactoring results in CLAUDE.md&lt;/h2&gt;
&lt;p&gt;Refactoring changes structure rather than adding new features, so the &lt;code&gt;CLAUDE.md&lt;/code&gt; that describes your structure needs to be updated to match too.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;The refactoring is done. Update the structure notes in CLAUDE.md as follows:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Added storage.js (handles localStorage logic)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Added hooks/useBookmarks.js (bookmark CRUD logic)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- App.jsx now handles UI layout only&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- The loadData() function was removed&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When your docs stay in sync with the code, later on you can just ask the AI “what does the current structure look like?” and get an accurate picture of the current state right away.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;when-to-stop-refactoring&quot;&gt;When to stop refactoring&lt;/h2&gt;
&lt;p&gt;Refactoring never really ends. There’s always more you could improve. But you can’t spend all your time refactoring while you’re also trying to run a live service.&lt;/p&gt;
&lt;p&gt;Here are the signs that it’s fine to stop refactoring and move on to the next feature:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When you ask the AI for a new feature, it gets added cleanly without conflicting with the existing code&lt;/li&gt;
&lt;li&gt;When you need to fix something, you can immediately tell which file to open&lt;/li&gt;
&lt;li&gt;Each code file is roughly 200 lines or less&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Perfectly tidy code isn’t the goal. The goal is &lt;strong&gt;a state where adding the next feature or fixing a bug doesn’t feel like a hassle&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;next-time&quot;&gt;Next time&lt;/h2&gt;
&lt;p&gt;In the next post, we’ll cover something you’re bound to run into after launch: production bugs. Bugs that show up in the environment real users are actually using behave differently from what you see in local development. We’ll look at how to track down the cause and fix it together with AI.&lt;/p&gt;</content:encoded><category>Vibe Coding for Everyone</category><category>Refactoring</category><category>Vibe Coding</category><category>Claude Code</category></item><item><title>useRef — Remembering a Value Without Touching the Screen</title><link>https://blog.bepuljang.co.kr/en/react/07/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/react/07/</guid><description>A look at useRef, which unlike State doesn&apos;t trigger a re-render when it changes. How it differs from a plain variable and a module-level let, why you shouldn&apos;t read or write it during render, and its most common use — reaching into the DOM.</description><pubDate>Fri, 10 Jul 2026 06:41:13 GMT</pubDate><content:encoded>&lt;p&gt;The State we’ve covered so far was a value that “redraws the screen when it changes.” But you can have the exact opposite need: a value you want to &lt;strong&gt;remember across renders, yet whose change should not redraw the screen&lt;/strong&gt;. That’s what &lt;strong&gt;useRef&lt;/strong&gt; is for.&lt;/p&gt;
&lt;p&gt;For example, values like “the previous scroll position,” “a counter for how many times it rendered,” or “a timer’s ID” need to be remembered but don’t appear on screen directly. Put these in State and you’d needlessly redraw the screen every time they change.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;useref--a-memory-box-with-no-effect-on-rendering&quot;&gt;useRef — a memory box with no effect on rendering&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { useRef } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;react&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; ref&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useRef&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(initialValue);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;useRef&lt;/code&gt; returns an object shaped like &lt;code&gt;{ current: initialValue }&lt;/code&gt;. You read and write the value through &lt;code&gt;ref.current&lt;/code&gt;, and this box has two properties:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;changing it does not trigger a re-render&lt;/strong&gt; (the decisive difference from State)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;its value persists across re-renders&lt;/strong&gt; (the difference from a plain local variable)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Set side by side with State, the character is clear.&lt;/p&gt;























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Change → re-render?&lt;/th&gt;&lt;th&gt;Persists across re-renders?&lt;/th&gt;&lt;th&gt;Use&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;State&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;✅ yes&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;td&gt;values that must reflect on screen&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;useRef&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;❌ no&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;td&gt;values to remember, unrelated to the screen&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&quot;why-not-just-a-variable-or-a-module-level-let&quot;&gt;Why not just a variable or a module-level &lt;code&gt;let&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;“If it’s a remembered value that doesn’t touch the screen, can’t I just put a &lt;code&gt;let&lt;/code&gt; outside the component?” you’d think. And indeed you &lt;em&gt;can&lt;/em&gt; make a ‘persisting value that doesn’t re-render’ that way. But there’s a decisive problem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A &lt;code&gt;let&lt;/code&gt; outside the module is shared by all instances of that component.&lt;/strong&gt; Show the same component three times on screen and the three share one variable, overwriting each other’s value. &lt;code&gt;useRef&lt;/code&gt;, by contrast, gives &lt;strong&gt;each component instance its own independent box.&lt;/strong&gt; Three on screen means three boxes, with no interference.&lt;/p&gt;
&lt;p&gt;A local variable &lt;em&gt;inside&lt;/em&gt; the component has the opposite problem. It’s independent per instance, but as we saw it runs again on every render and &lt;strong&gt;resets each time&lt;/strong&gt;. useRef is the one place that satisfies both properties at once: “independent per instance, yet persisting across renders.”&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;dont-read-or-write-it-during-render&quot;&gt;Don’t read or write it during render&lt;/h2&gt;
&lt;p&gt;useRef has one important rule: &lt;strong&gt;don’t read or write &lt;code&gt;ref.current&lt;/code&gt; during rendering.&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; MyComponent&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  myRef.current &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 123&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;              &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 🚩 no writing during render&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{myRef.current}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;;  &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 🚩 no reading during render&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In Part 5 we said rendering must be &lt;strong&gt;pure&lt;/strong&gt;. But a ref is a value that doesn’t notify React when it changes, so reading or writing it during render makes the screen depend on “a value you can’t tell when changed” — unpredictable. Instead, refs should be handled &lt;strong&gt;inside event handlers or effects.&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; MyComponent&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    myRef.current &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 123&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;           &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ✅ fine inside an effect&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; handleClick&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;    doSomething&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(myRef.current);    &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ✅ fine inside an event handler&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Touching it once, after rendering is done, when some event occurs — that’s the right rhythm for a ref.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;the-most-common-use--reaching-into-a-dom-element&quot;&gt;The most common use — reaching into a DOM element&lt;/h2&gt;
&lt;p&gt;Actually, the place you’ll meet useRef most in practice is elsewhere: &lt;strong&gt;when you need to point directly at a specific DOM element.&lt;/strong&gt; Automatically focusing an input, measuring an element’s size, moving the scroll — things like that.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; SearchBox&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; inputRef&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useRef&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;  useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    inputRef.current.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;focus&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(); &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// focus the input on mount&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  }, []);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;input&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; ref&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{inputRef} /&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Attach &lt;code&gt;ref={inputRef}&lt;/code&gt; to a JSX element and, after render, React puts that actual DOM node into &lt;code&gt;inputRef.current&lt;/code&gt;. Even while you draw the screen declaratively, useRef is the official escape hatch for those moments when you must &lt;strong&gt;handle an element imperatively.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;state-or-ref&quot;&gt;State or Ref?&lt;/h2&gt;
&lt;p&gt;One last rule of thumb. &lt;strong&gt;If the value must be visible on screen, use State; if it just needs to be remembered, unrelated to the screen, use Ref.&lt;/strong&gt; A common mistake is to confuse the two — putting “a value that should show on screen” in a ref so the screen never updates, or putting “a value unrelated to the screen” in state so it triggers needless re-renders.&lt;/p&gt;
&lt;p&gt;We’ve now come through the values a single component handles on its own (State and Ref). Next time we’ll widen the view to how &lt;strong&gt;distant components share a value&lt;/strong&gt; — going one step beyond Props, into &lt;strong&gt;Context&lt;/strong&gt;.&lt;/p&gt;</content:encoded><category>React — Web Development from Start to Deploy</category><category>React</category><category>useRef</category><category>Hooks</category><category>DOM</category></item><item><title>The useEffect Cleanup Function — the Art of Undoing What You Turned On</title><link>https://blog.bepuljang.co.kr/en/react/06/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/react/06/</guid><description>When and why useEffect&apos;s cleanup function runs, laid out as a lifecycle. Including the real reason behind the commonly-misunderstood &apos;double run in development&apos; (StrictMode).</description><pubDate>Fri, 10 Jul 2026 06:39:07 GMT</pubDate><content:encoded>&lt;p&gt;Last time we said, “if you start a timer or connect to a server, you need to turn it off when the component goes away.” The thing in charge of that &lt;strong&gt;turning off&lt;/strong&gt; is the cleanup function. The concept itself is simple, but its timing is confusing — which makes it the single biggest source of confusion in useEffect. Today let’s nail down that timing.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-is-a-cleanup-function&quot;&gt;What is a cleanup function?&lt;/h2&gt;
&lt;p&gt;A cleanup function is &lt;strong&gt;the function that the setup function returns&lt;/strong&gt;. That’s the whole syntax.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; connection&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; createConnection&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(roomId);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  connection.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;connect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;();          &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// setup: connect&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; connection.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;disconnect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(); &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// cleanup: disconnect&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}, [roomId]);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The function after &lt;code&gt;return&lt;/code&gt; is the cleanup function. As the name says, it &lt;strong&gt;undoes&lt;/strong&gt; whatever the setup function set in motion (connections, subscriptions, timers, and so on). The key intuition is that “the cleanup function is the setup function’s &lt;strong&gt;pair&lt;/strong&gt;.” If setup turns something on, cleanup turns it off; if setup subscribes, cleanup unsubscribes.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;when-does-it-run--the-three-moments&quot;&gt;When does it run — the three moments&lt;/h2&gt;
&lt;p&gt;React calls setup and cleanup whenever needed, which comes down to three moments:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Mount&lt;/strong&gt; (the component first appears on screen): the setup function runs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;When a dependency changes and it re-runs&lt;/strong&gt;: first the &lt;strong&gt;cleanup runs with the old values&lt;/strong&gt;, then the &lt;strong&gt;setup runs with the new values&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unmount&lt;/strong&gt; (the component disappears from screen): the cleanup runs one final time.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Number 2 is especially important. In the example above, when &lt;code&gt;roomId&lt;/code&gt; changes from &lt;code&gt;room 1&lt;/code&gt; to &lt;code&gt;room 2&lt;/code&gt;, React works in the order &lt;strong&gt;“disconnect from room 1 → connect to room 2.”&lt;/strong&gt; Because it cleans up the old connection &lt;em&gt;before&lt;/em&gt; making the new one, you don’t get the accident of the old connection lingering and piling up when you switch rooms.&lt;/p&gt;
&lt;p&gt;So the cleanup function doesn’t run “only when the component dies” — it also runs &lt;strong&gt;right before each time setup re-runs&lt;/strong&gt;, to undo the old one.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;why-it-runs-twice-in-development&quot;&gt;Why it runs twice in development&lt;/h2&gt;
&lt;p&gt;Here’s where many people get badly confused. In a development environment, when you make an effect, setup appears to be called &lt;strong&gt;twice&lt;/strong&gt; on mount (setup → cleanup → setup). Add a log and it looks like initialization runs twice, which is alarming.&lt;/p&gt;
&lt;p&gt;This is &lt;strong&gt;not&lt;/strong&gt; a bug, and &lt;strong&gt;not&lt;/strong&gt; due to JavaScript’s async behavior. It’s React’s &lt;strong&gt;StrictMode&lt;/strong&gt; deliberately running setup→cleanup→setup one extra time, in development only. The purpose is singular: &lt;strong&gt;a stress test to surface effects that are missing or have a broken cleanup function early.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The logic goes like this. If your cleanup perfectly undoes your setup, then “setup called once (production)” and “setup→cleanup→setup (development)” should be &lt;strong&gt;indistinguishable&lt;/strong&gt; to the user. If the double run causes a visible problem (a duplicate connection, an event registered twice, etc.), that’s React telling you: &lt;strong&gt;your cleanup function is missing some logic.&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;So the development double-run isn’t something to eliminate — it’s an &lt;strong&gt;opportunity to have your cleanup logic verified&lt;/strong&gt;. In a production build the double run disappears and setup runs only once.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;so-heres-an-easy-way-to-see-the-cleanup-function&quot;&gt;So here’s an easy way to see the cleanup function&lt;/h2&gt;
&lt;p&gt;See the cleanup function as “an occasional tidy-up you sometimes need” and its timing will keep confusing you. Instead, see it this way: &lt;strong&gt;every setup function should come as a pair with a cleanup function that undoes it.&lt;/strong&gt; Connect, then disconnect; subscribe, then unsubscribe; set a timer, then clear it — keep only this symmetry, and no matter when React runs the pair (before a re-run, on unmount, or in the development double-run), it always behaves correctly.&lt;/p&gt;
&lt;p&gt;We’ve now come through State and Effect. Next time we’ll look at a value with a different character from state — one that &lt;strong&gt;doesn’t redraw the screen when it changes&lt;/strong&gt;, yet remembers its value across renders: &lt;strong&gt;useRef&lt;/strong&gt;.&lt;/p&gt;</content:encoded><category>React — Web Development from Start to Deploy</category><category>React</category><category>useEffect</category><category>cleanup</category><category>StrictMode</category></item><item><title>useEffect — Handling the Work Outside Rendering</title><link>https://blog.bepuljang.co.kr/en/react/05/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/react/05/</guid><description>A look at React&apos;s useEffect, starting from the concept of a &apos;side effect.&apos; How it differs from rendering, when each of the three dependency-array forms runs, and the common misunderstandings.</description><pubDate>Fri, 10 Jul 2026 06:36:13 GMT</pubDate><content:encoded>&lt;p&gt;At the end of last time I left this question: when state changes, what if you want to do &lt;strong&gt;something other than draw the screen&lt;/strong&gt;? Like sending a request to the server every time the search term changes. The hook for that “work outside the screen” is &lt;strong&gt;useEffect&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If useState is the side that “changes state to update the screen,” useEffect is the opposite side — one that “&lt;strong&gt;performs some action&lt;/strong&gt; in step with state or rendering.”&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-is-a-side-effect&quot;&gt;What is a side effect?&lt;/h2&gt;
&lt;p&gt;In Part 1 we said a component is “a function that takes state and returns a screen.” That return — rendering — must be &lt;strong&gt;pure&lt;/strong&gt;. The same input yields the same screen, and it doesn’t touch the outside world along the way; that’s what makes it predictable.&lt;/p&gt;
&lt;p&gt;But real apps are constantly entangled with the outside world. They fetch data from a server, start timers, subscribe to browser events, write logs. Work that happens &lt;strong&gt;outside rendering’s pure calculation&lt;/strong&gt; like this is called a side effect. useEffect is the tool that lifts such work out of the render flow and runs it safely &lt;strong&gt;after the screen has been drawn&lt;/strong&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { useEffect } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;react&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;basic-syntax&quot;&gt;Basic syntax&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;  // perform the side effect here&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}, [dependencies]);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are two arguments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;first: the &lt;strong&gt;function to run&lt;/strong&gt; (the setup function)&lt;/li&gt;
&lt;li&gt;second: the &lt;strong&gt;dependency array&lt;/strong&gt; — the list of values that decides when this effect re-runs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The key is this: an effect runs not &lt;strong&gt;during&lt;/strong&gt; rendering but &lt;strong&gt;right after&lt;/strong&gt; the screen is drawn. And not every time — it re-runs only &lt;strong&gt;when a value in the dependency array has changed&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;the-dependency-array-is-everything&quot;&gt;The dependency array is everything&lt;/h2&gt;
&lt;p&gt;useEffect’s behavior comes down to how you set that second argument. There are three cases.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 1) [value] — once after the first render + whenever that value changes&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  console.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;count changed:&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, count);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}, [count]);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 2) [] — exactly once, after the first render (for setup)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  console.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;component first appeared&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}, []);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 3) array omitted — every single render&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  console.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;log&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;something rendered&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Second argument&lt;/th&gt;&lt;th&gt;When it runs&lt;/th&gt;&lt;th&gt;Main use&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;[a, b]&lt;/code&gt;&lt;/td&gt;&lt;td&gt;after first render + when &lt;code&gt;a&lt;/code&gt; or &lt;code&gt;b&lt;/code&gt; changes&lt;/td&gt;&lt;td&gt;reacting to specific value changes&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/td&gt;&lt;td&gt;once after first render&lt;/td&gt;&lt;td&gt;initial setup (subscriptions, first load)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;(none)&lt;/td&gt;&lt;td&gt;every render&lt;/td&gt;&lt;td&gt;rarely used&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;One common point of confusion. &lt;strong&gt;Whether or not there’s a dependency array, an effect runs at least once, right after the first render.&lt;/strong&gt; Writing &lt;code&gt;[count]&lt;/code&gt; doesn’t mean it runs “only when count changes” — it’s “once at first + whenever count changes after.” If you don’t need that initial run, you have to filter it out with a condition inside the effect.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;a-common-misconception-dont-use-it-everywhere&quot;&gt;A common misconception: don’t use it everywhere&lt;/h2&gt;
&lt;p&gt;useEffect is powerful, so the newer you are, the easier it is to overuse. But these two are classic cases where &lt;strong&gt;you don’t need useEffect&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A value computed from other state&lt;/strong&gt;: for example, if you build &lt;code&gt;fullName&lt;/code&gt; from &lt;code&gt;firstName&lt;/code&gt; and &lt;code&gt;lastName&lt;/code&gt;, you don’t need an effect to make another piece of state — just compute it during render.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Responding to a user event&lt;/strong&gt;: reacting to a button click is the job of an &lt;strong&gt;event handler&lt;/strong&gt; like &lt;code&gt;onClick&lt;/code&gt;, not an effect.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Remember useEffect as the tool you reach for when you need “&lt;strong&gt;synchronization with the outside world&lt;/strong&gt; that rendering alone can’t do,” and you’ll avoid overusing it.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;where-theres-a-setup-theres-cleanup&quot;&gt;Where there’s a setup, there’s cleanup&lt;/h2&gt;
&lt;p&gt;If you start a timer or connect to a server in useEffect, you need to &lt;strong&gt;turn those off&lt;/strong&gt; when the component goes away. Turning things on without ever turning them off leads to problems like memory leaks or duplicate connections.&lt;/p&gt;
&lt;p&gt;For this, useEffect lets the setup function &lt;strong&gt;return another function&lt;/strong&gt;, so you can attach a ‘cleanup’ step.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;useEffect&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; setInterval&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(tick, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1000&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;); &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// setup: start the timer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; clearInterval&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(id);      &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// cleanup: stop the timer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}, []);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Exactly when and why this cleanup function runs is quite confusing at first. So next time we’ll take &lt;strong&gt;useEffect’s cleanup function&lt;/strong&gt; on its own and cover it in detail.&lt;/p&gt;</content:encoded><category>React — Web Development from Start to Deploy</category><category>React</category><category>useEffect</category><category>Side Effects</category><category>Hooks</category></item><item><title>State and useState — the Value a Component Remembers on Its Own</title><link>https://blog.bepuljang.co.kr/en/react/04/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/react/04/</guid><description>A look at React&apos;s State in contrast to Props. Why a plain variable won&apos;t do, why you receive useState as a const, and why you must always change the value through the setter.</description><pubDate>Fri, 10 Jul 2026 01:37:31 GMT</pubDate><content:encoded>&lt;p&gt;Last time we said Props are values &lt;strong&gt;received from outside (the parent) that can’t be changed&lt;/strong&gt;. And it left us with a natural question: how do we handle a value the component &lt;strong&gt;holds itself and that changes over time&lt;/strong&gt;? The answer is today’s subject — &lt;strong&gt;State&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;A counter that goes up each time you click, the characters typed into an input, a menu that opens and closes — every value that changes as the user interacts is State.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;why-wont-a-plain-variable-do&quot;&gt;Why won’t a plain variable do?&lt;/h2&gt;
&lt;p&gt;“If it’s just a changing value, can’t I use a regular variable?” you’d think. But inside a component, a plain variable fails at two things.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Counter&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  let&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; count &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; onClick&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { count &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;+=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;; }}&gt;{count}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;No matter how you click this button, the number on screen &lt;strong&gt;doesn’t budge from 0.&lt;/strong&gt; For two reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;When it changes, the screen isn’t redrawn.&lt;/strong&gt; In Part 1 we said React “redraws the screen when state changes” — but a plain variable isn’t watched by React, so it never knows it changed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;If it were redrawn, the value would reset.&lt;/strong&gt; Even if it somehow redrew, the component function runs from the top again, so &lt;code&gt;let count = 0&lt;/code&gt; runs again and returns to 0.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So a component needs a special kind of variable that &lt;strong&gt;remembers its value across re-renders&lt;/strong&gt; and &lt;strong&gt;redraws the screen when that value changes&lt;/strong&gt;. That’s State, and the tool that makes it is &lt;strong&gt;useState&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;how-to-use-usestate&quot;&gt;How to use useState&lt;/h2&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; { useState } &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;from&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;react&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Counter&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;count&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;setCount&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useState&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; onClick&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; setCount&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(count &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)}&gt;{count}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;useState&lt;/code&gt; takes an initial value and returns &lt;strong&gt;an array of length 2&lt;/strong&gt;, which you receive by destructuring:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;first (&lt;code&gt;count&lt;/code&gt;): the current state value&lt;/li&gt;
&lt;li&gt;second (&lt;code&gt;setCount&lt;/code&gt;): a dedicated function (setter) that changes it&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The names are up to you, but by convention you pair them as &lt;code&gt;x&lt;/code&gt;, &lt;code&gt;setX&lt;/code&gt;. Now clicking the button calls &lt;code&gt;setCount&lt;/code&gt;, which changes the state, and the component redraws accordingly, showing the new number.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;These special functions that start with &lt;code&gt;use&lt;/code&gt; are called &lt;strong&gt;Hooks&lt;/strong&gt;. Think of it as a function component “hooking into” React features like State and side effects. A good part of this series is a story about these hooks.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;why-receive-it-as-const-not-let&quot;&gt;Why receive it as &lt;code&gt;const&lt;/code&gt;, not &lt;code&gt;let&lt;/code&gt;?&lt;/h2&gt;
&lt;p&gt;Receiving a &lt;em&gt;changing&lt;/em&gt; value as &lt;code&gt;const&lt;/code&gt; looks odd. “It’s going to change, shouldn’t it be &lt;code&gt;let&lt;/code&gt;?” But hidden here is a misunderstanding about how React renders.&lt;/p&gt;
&lt;p&gt;The key is this: &lt;strong&gt;&lt;code&gt;count&lt;/code&gt; never changes within a single render.&lt;/strong&gt; State changing doesn’t mean editing that value in place — it means &lt;strong&gt;running the component function again from the top.&lt;/strong&gt; In that new run, &lt;code&gt;useState&lt;/code&gt; returns the updated value this time, and &lt;code&gt;const count&lt;/code&gt; is &lt;strong&gt;freshly bound&lt;/strong&gt; to that new value.&lt;/p&gt;
&lt;p&gt;So the flow isn’t “keep editing one &lt;code&gt;count&lt;/code&gt;” but “a fresh &lt;code&gt;count&lt;/code&gt; is created for each render.” Within each render, &lt;code&gt;count&lt;/code&gt; is a fixed snapshot, so &lt;code&gt;const&lt;/code&gt; is exactly the right description. In fact, if you made it &lt;code&gt;let&lt;/code&gt; and edited &lt;code&gt;count = ...&lt;/code&gt; directly, React would have no idea about the change and would overwrite it on the next render — pure confusion.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;always-change-the-value-through-the-setter&quot;&gt;Always change the value through the setter&lt;/h2&gt;
&lt;p&gt;The single most important practical rule follows from that: &lt;strong&gt;never edit state directly; always change it through the setter.&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;count &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; count &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;        &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ❌ React doesn&apos;t know → screen doesn&apos;t change&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;setCount&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(count &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);      &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ✅ change state + schedule a re-render&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;setCount&lt;/code&gt; doesn’t just change the value — it &lt;strong&gt;schedules&lt;/strong&gt; with React: “redraw this component.” That one line binds value-change and screen-update into one, which is the very reason State exists.&lt;/p&gt;
&lt;p&gt;The same goes for object or array state. Don’t edit the insides (&lt;code&gt;user.name = ...&lt;/code&gt;); &lt;strong&gt;make a new object&lt;/strong&gt; and pass it to the setter.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;setUser&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;...&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;user, name: &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;New name&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;when-basing-it-on-the-previous-value-pass-a-function&quot;&gt;When basing it on the previous value, pass a function&lt;/h2&gt;
&lt;p&gt;One practical tip. When deciding the next value based on the previous state, it’s safer to pass a &lt;strong&gt;function&lt;/strong&gt; instead of a value.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;setCount&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;prev&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; prev &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Give &lt;code&gt;setCount&lt;/code&gt; a function and React feeds in the &lt;strong&gt;latest state&lt;/strong&gt; as &lt;code&gt;prev&lt;/code&gt;. Even when state changes pile up — say you click a button rapidly several times — the values don’t get tangled, preventing bugs like “why did it only go up once?”&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;To sum up, State is a value that &lt;strong&gt;persists across re-renders and redraws the screen when it changes&lt;/strong&gt;, and you always change it through the setter. By now the flow of “when state changes, the screen redraws” should feel concrete.&lt;/p&gt;
&lt;p&gt;But what if, when state changes, you want to do &lt;strong&gt;something other than draw the screen&lt;/strong&gt;? Say you need to send a request to the server every time the search term (state) changes. The tool for that “work outside the screen” is the next article’s subject — &lt;strong&gt;useEffect&lt;/strong&gt;.&lt;/p&gt;</content:encoded><category>React — Web Development from Start to Deploy</category><category>React</category><category>State</category><category>useState</category><category>Hooks</category></item><item><title>Props — the Attributes a Parent Hands to a Child</title><link>https://blog.bepuljang.co.kr/en/react/03/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/react/03/</guid><description>A look at React&apos;s Props framed against HTML attributes. How they act like function arguments, destructuring, children, and why the rule &apos;Props are read-only&apos; exists at all.</description><pubDate>Fri, 10 Jul 2026 01:34:20 GMT</pubDate><content:encoded>&lt;p&gt;At the end of last time I teased “how a parent hands values down to a child when composing components.” That’s exactly &lt;strong&gt;Props&lt;/strong&gt;. It looks easy, yet it’s a prime suspect for making things not work as intended once you start building — and most of that confusion comes from slightly misreading what Props actually are. Today let’s pin that down.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;props-are-a-components-attributes&quot;&gt;Props are a component’s ‘attributes’&lt;/h2&gt;
&lt;p&gt;Props is short for &lt;strong&gt;Properties&lt;/strong&gt; — “attributes.” It’s hardly an exaggeration to say the name is the whole story. On the web we’ve been using attributes all along.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;html&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;https://...&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;Link&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here &lt;code&gt;href&lt;/code&gt; is an attribute of the &lt;code&gt;a&lt;/code&gt; element. You give the tag a property, which decides how that element looks and behaves. In React, &lt;strong&gt;a component takes the place of that element.&lt;/strong&gt; And because a component is a function, attributes are passed &lt;strong&gt;like the function’s arguments.&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Welcome&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;props&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;Hello, {props.name}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; App&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Welcome&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; name&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;Sara&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; /&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Writing &lt;code&gt;&amp;#x3C;Welcome name=&quot;Sara&quot; /&gt;&lt;/code&gt; means the &lt;code&gt;Welcome&lt;/code&gt; function receives &lt;code&gt;{ name: &quot;Sara&quot; }&lt;/code&gt; as &lt;code&gt;props&lt;/code&gt;. The result is &lt;code&gt;&amp;#x3C;h1&gt;Hello, Sara&amp;#x3C;/h1&gt;&lt;/code&gt;. In other words, &lt;strong&gt;the things you write as JSX attributes get bundled into a single object and passed to the component function as its argument.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;in-practice-you-pull-them-out-by-destructuring&quot;&gt;In practice, you pull them out by destructuring&lt;/h2&gt;
&lt;p&gt;Prefixing everything with &lt;code&gt;props.&lt;/code&gt; — &lt;code&gt;props.name&lt;/code&gt;, &lt;code&gt;props.age&lt;/code&gt; — gets tedious, so in practice it’s far more common to &lt;strong&gt;destructure&lt;/strong&gt; just what you need right away.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Welcome&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;age&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{name}, you&apos;re {age}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The behavior is identical. You’ve simply unpacked the incoming &lt;code&gt;props&lt;/code&gt; object right there in the function’s signature. You can give defaults in the same place, too.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Welcome&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;Guest&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;Welcome, {name}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;props-are-read-only&quot;&gt;Props are read-only&lt;/h2&gt;
&lt;p&gt;The single most important rule: &lt;strong&gt;a child cannot change the Props it receives.&lt;/strong&gt; They’re read-only.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; MyComponent&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;name&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  name &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;Another name&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;; &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// 🚩 don&apos;t do this&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h3&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{name}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h3&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why the restriction? If you give ‘ink’ the attribute ‘black’ and define it as ‘black ink,’ that ink can’t turn its own color red on its own. An attribute is an &lt;strong&gt;identity the component received from the outside (its parent)&lt;/strong&gt;. If a child changed it freely, the promise the parent made — “this is how you should look” — would break.&lt;/p&gt;
&lt;p&gt;This rule ties into React’s big principle, &lt;strong&gt;one-way data flow&lt;/strong&gt;. Data always flows parent → child, top to bottom. Because values flow in only one direction, it’s easier to trace “where on earth did this value change?”, and that’s what makes the ‘predictable screen’ from earlier articles possible.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;children--what-sits-between-the-tags-is-also-a-prop&quot;&gt;children — what sits between the tags is also a Prop&lt;/h2&gt;
&lt;p&gt;Not just attributes: whatever you put &lt;strong&gt;between a component’s opening and closing tags&lt;/strong&gt; is passed as a Prop too. It arrives under the special name &lt;code&gt;children&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Card&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;({ &lt;/span&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;children&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; }) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; className&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&quot;card&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{children}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; App&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Card&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;Title&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;Body&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Card&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;&amp;#x3C;h2&gt;&lt;/code&gt; and &lt;code&gt;&amp;#x3C;p&gt;&lt;/code&gt; slot right into the &lt;code&gt;{children}&lt;/code&gt; spot inside &lt;code&gt;Card&lt;/code&gt;. This lets you cleanly separate the outer shell (a card, modal, layout, etc.) from the content that goes inside it.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;lets-not-call-props-a-communication-channel&quot;&gt;Let’s not call Props a ‘communication channel’&lt;/h2&gt;
&lt;p&gt;As we wrap up, one perspective worth correcting. Props are often explained as “the way a parent sends values to a child” — not wrong, but with the order reversed.&lt;/p&gt;
&lt;p&gt;The essence of Props is always &lt;strong&gt;defining a child component’s attributes&lt;/strong&gt;. Since the values that define those attributes happen to be handed down by the parent, they &lt;strong&gt;end up&lt;/strong&gt; being used for top-down data passing as well. See them not as “a tool for carrying values” but as “a spec the parent writes for how the child should look,” and the “doesn’t work as intended” moments from the opening drop sharply. The read-only rule feels natural from this view, too.&lt;/p&gt;
&lt;p&gt;But a natural question arises here. If Props are values received from outside that &lt;strong&gt;can’t be changed&lt;/strong&gt;, how do we handle a value the component &lt;strong&gt;holds itself and that changes over time&lt;/strong&gt;? That’s exactly the subject of the next article — &lt;strong&gt;State&lt;/strong&gt;.&lt;/p&gt;</content:encoded><category>React — Web Development from Start to Deploy</category><category>React</category><category>Props</category><category>Components</category><category>One-way Data Flow</category></item><item><title>JSX — It Looks Like HTML, but It&apos;s Actually JavaScript</title><link>https://blog.bepuljang.co.kr/en/react/02/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/react/02/</guid><description>A close look at JSX, the syntax React uses to describe the screen. Why it resembles HTML yet differs, what the curly braces do, and why you write className instead of class — framed against your web experience.</description><pubDate>Fri, 10 Jul 2026 01:24:02 GMT</pubDate><content:encoded>&lt;p&gt;Last time we said a component is “a function that returns a blueprint for drawing the screen.” But you were probably wondering about that blueprint itself — the HTML-looking thing that comes after &lt;code&gt;return&lt;/code&gt;. Tags sitting inside a function definitely look strange the first time. The name of that syntax is &lt;strong&gt;JSX&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;For anyone with web experience, JSX is a double-edged sword. It resembles HTML, which lowers the barrier to entry — but by exactly that much, it trips you up when you wave it off as “probably just HTML.” So today let’s cleanly separate what JSX shares with HTML and what it doesn’t.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;jsx-isnt-html--its-javascript&quot;&gt;JSX isn’t HTML — it’s JavaScript&lt;/h2&gt;
&lt;p&gt;JSX is short for &lt;strong&gt;JavaScript XML&lt;/strong&gt;. The XML in the name makes it look like markup, but the heart of it is the &lt;strong&gt;JavaScript&lt;/strong&gt; in front. JSX isn’t a separate markup language; it’s a &lt;strong&gt;syntax extension that lets you write markup inside JavaScript&lt;/strong&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; element&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;Hello&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The browser doesn’t understand this line as-is. During the build it’s &lt;strong&gt;converted into a plain JavaScript function call&lt;/strong&gt;, like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; element&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; React.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;createElement&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;h1&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;null&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;Hello&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So writing a tag is really calling a function, and JSX is just the nice-looking shell that lets you write it readably. The accurate mental model isn’t “HTML bolted onto JS” but &lt;strong&gt;“JS borrowing a syntax that looks like markup.”&lt;/strong&gt; Hold onto this and every difference below explains itself.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;By the way, since React 17 the conversion changed, so you no longer need to write &lt;code&gt;import React&lt;/code&gt; at the top of a file just to use JSX. That’s why you’ll see that import in older code.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;curly-braces---the-door-back-into-javascript&quot;&gt;Curly braces &lt;code&gt;{}&lt;/code&gt; — the door back into JavaScript&lt;/h2&gt;
&lt;p&gt;Because JSX &lt;em&gt;is&lt;/em&gt; JavaScript, you can step back out into JavaScript in the middle of markup. That doorway is the &lt;strong&gt;curly braces &lt;code&gt;{}&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; name&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;Karina&quot;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; element&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;Hello, {name}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Inside &lt;code&gt;{}&lt;/code&gt; you can put not just variables but any &lt;strong&gt;expression that produces a value&lt;/strong&gt; — arithmetic, function calls, and so on.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; +&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 2&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;              {&lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;/* 3 */&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;{user.isAdmin &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;Admin&apos;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &apos;Member&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But only “expressions that become a value” are allowed. &lt;strong&gt;Statements&lt;/strong&gt; like &lt;code&gt;if&lt;/code&gt; or &lt;code&gt;for&lt;/code&gt; can’t go in there. This distinction is annoying at first, but once you think of “a &lt;code&gt;{}&lt;/code&gt; inside JSX as a slot that expects a single value,” it clicks quickly.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;the-little-things-that-differ-from-html&quot;&gt;The little things that differ from HTML&lt;/h2&gt;
&lt;p&gt;For all the resemblance, there are points where they diverge — and every reason boils down to &lt;strong&gt;“JSX is JavaScript.”&lt;/strong&gt;&lt;/p&gt;



































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;HTML&lt;/th&gt;&lt;th&gt;JSX&lt;/th&gt;&lt;th&gt;Why&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;class=&quot;box&quot;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;className=&quot;box&quot;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;class&lt;/code&gt; is a reserved word in JavaScript&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;for=&quot;id&quot;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;htmlFor=&quot;id&quot;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;for&lt;/code&gt; is reserved too (the loop)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;onclick=&quot;...&quot;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;onClick={...}&lt;/code&gt;&lt;/td&gt;&lt;td&gt;attributes are camelCase, value is a function&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;style=&quot;color:red&quot;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;style={{ color: &apos;red&apos; }}&lt;/code&gt;&lt;/td&gt;&lt;td&gt;an &lt;strong&gt;object&lt;/strong&gt;, not a string&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;&amp;#x3C;br&gt;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;&amp;#x3C;br /&gt;&lt;/code&gt;&lt;/td&gt;&lt;td&gt;every tag must be closed&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Look closely at &lt;code&gt;style&lt;/code&gt;. The outer &lt;code&gt;{}&lt;/code&gt; is “the door into JavaScript,” the inner &lt;code&gt;{}&lt;/code&gt; is “an object,” so there are two layers of braces. A hyphenated property like &lt;code&gt;background-color&lt;/code&gt; can’t be a JavaScript object key, so you write it camelCase as &lt;code&gt;backgroundColor&lt;/code&gt;. It resembles web styling, but it’s ultimately a &lt;strong&gt;JavaScript object&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;the-return-must-be-a-single-piece&quot;&gt;The return must be a single piece&lt;/h2&gt;
&lt;p&gt;A component &lt;code&gt;return&lt;/code&gt;s a screen blueprint — and its &lt;strong&gt;top level must be a single element&lt;/strong&gt;. Just as a function returns one value, JSX too must gather into one result.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// ❌ can&apos;t return two sibling elements side by side&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;Title&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;Body&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But it’d be a shame to wrap things in a meaningless &lt;code&gt;&amp;#x3C;div&gt;&lt;/code&gt; just to satisfy that and clutter the markup. That’s what the &lt;strong&gt;Fragment&lt;/strong&gt;, the empty tag &lt;code&gt;&amp;#x3C;&gt;...&amp;#x3C;/&gt;&lt;/code&gt;, is for.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;#x3C;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;Title&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;Body&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  &amp;#x3C;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;&amp;#x3C;&gt;&lt;/code&gt; groups several siblings into one without leaving any actual element on the screen.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;why-build-it-this-way-at-all&quot;&gt;Why build it this way at all?&lt;/h2&gt;
&lt;p&gt;Mixing markup and logic in one file, one function, feels odd at first. For a long time we were taught to keep HTML with HTML and JavaScript with JavaScript, as a virtue.&lt;/p&gt;
&lt;p&gt;But recall the idea of the component from last time and the story changes. When you build a single button, its &lt;strong&gt;appearance (markup)&lt;/strong&gt; and its &lt;strong&gt;behavior (logic)&lt;/strong&gt; are really one inseparable lump. JSX takes the view that “the screen and the logic that makes it were one body all along” and turns it into syntax. You’re gathering code by &lt;strong&gt;concern (a single component)&lt;/strong&gt; rather than by file type.&lt;/p&gt;
&lt;p&gt;Now you’ve got a feel for how the screen gets written. Next time we’ll look at how, when composing these components, a parent hands values down to a child — &lt;strong&gt;Props&lt;/strong&gt;.&lt;/p&gt;</content:encoded><category>React — Web Development from Start to Deploy</category><category>React</category><category>JSX</category><category>Frontend</category><category>Declarative UI</category></item><item><title>What Is React? — Declarative UI and the Idea of Components</title><link>https://blog.bepuljang.co.kr/en/react/01/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/react/01/</guid><description>What problem did React set out to solve, and why does the shift &apos;from imperative to declarative&apos; matter so much? A look at React&apos;s core idea, framed against your web-development experience.</description><pubDate>Thu, 09 Jul 2026 15:25:42 GMT</pubDate><content:encoded>&lt;p&gt;If you’ve done any web development at all, the name React is familiar. It shows up in job posts, in tutorials, in other people’s dependency lists. And yet, when someone asks “what &lt;em&gt;is&lt;/em&gt; React?”, most of us stall out somewhere around “uh… a frontend framework?”&lt;/p&gt;
&lt;p&gt;This series walks through React’s core concepts &lt;strong&gt;framed against your web-development experience&lt;/strong&gt;. It assumes you’ve already built something with HTML, CSS, and JavaScript, and it tells the story in the shape of “the old way was awkward here — React solves it like this.” As a first step, today we look at what React actually showed up to solve.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;react-is-a-library-not-a-framework&quot;&gt;React is a ‘library’, not a ‘framework’&lt;/h2&gt;
&lt;p&gt;First, a small but important distinction. React introduces itself as &lt;strong&gt;“a library for building user interfaces.”&lt;/strong&gt; The word &lt;em&gt;library&lt;/em&gt; rather than &lt;em&gt;framework&lt;/em&gt; is the key: it means React doesn’t decide everything — like routing or data management — for you.&lt;/p&gt;
&lt;p&gt;React is responsible for exactly one thing: &lt;strong&gt;drawing the view&lt;/strong&gt;. Changing pages based on the URL, talking to a server, managing global state — those you handle by picking and combining separate libraries. That makes React lightweight, but it also leaves “how to assemble it” up to you. Keep this trait in mind and the many options that appear later will make far more sense.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;from-imperative-to-declarative&quot;&gt;From imperative to declarative&lt;/h2&gt;
&lt;p&gt;The fastest way to understand React is to grab hold of one word: &lt;strong&gt;declarative&lt;/strong&gt;. What that means becomes clear when you compare it to the old way.&lt;/p&gt;
&lt;p&gt;Say we’re building a screen where “pressing a button increases a number by 1.” The old way, we did roughly this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;js&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;let&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; count &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; el&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; document.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;querySelector&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;#count&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;button.&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;addEventListener&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;&apos;click&apos;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, () &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  count &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;+=&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  el.textContent &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; count; &lt;/span&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;// fix the screen &apos;directly&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here the developer’s job is to &lt;strong&gt;spell out, step by step&lt;/strong&gt;, “the value changed, so go fix this part of the screen like so.” This is imperative. As screens grow complex, the number of “if this changes, fix that” cases explodes — and this is where most bugs are born.&lt;/p&gt;
&lt;p&gt;React flips the idea. Instead of writing the procedure for fixing the screen, the developer only declares &lt;strong&gt;“when the state is this, the screen looks like this.”&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; Counter&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  const&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; [&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;count&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;setCount&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;] &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; useState&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; onClick&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;{() &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; setCount&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;(count &lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;+&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;)}&gt;{count}&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When &lt;code&gt;count&lt;/code&gt; changes, &lt;strong&gt;React works out&lt;/strong&gt; where and how to update the screen. The developer only writes the relationship: “if the state is this, the result is that.” As a cooking analogy: if imperative is a recipe’s step-by-step (“put the pot on the heat, stir after three minutes”), declarative is closer to handing over a photo of the finished dish.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;components--the-screen-as-assemblable-units&quot;&gt;Components — the screen as assemblable units&lt;/h2&gt;
&lt;p&gt;The second core idea is the &lt;strong&gt;component&lt;/strong&gt;. Like &lt;code&gt;Counter&lt;/code&gt; above, in React you define a piece of the screen as &lt;strong&gt;a single function&lt;/strong&gt;. That function returns a screen (more precisely, a blueprint for drawing one), and we combine these pieces like Lego blocks to build the whole.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;jsx&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt; App&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#F97583&quot;&gt;  return&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Header&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Counter&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;      &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;Footer&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; /&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;    &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;color:#85E89D&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  );&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A well-built &lt;code&gt;Counter&lt;/code&gt; can be reused in many places, and each piece only has to answer for what happens inside itself. This view of the screen — not as one giant document but as a composition of small parts — is actually a shared language across modern frameworks like Vue and Svelte too, not just React.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;how-react-updates-the-screen-as-little-as-possible&quot;&gt;How React updates the screen as little as possible&lt;/h2&gt;
&lt;p&gt;If “when state changes, redraw the screen” is the rule, you might worry: isn’t redrawing the whole thing every time slow? This is where the &lt;strong&gt;Virtual DOM&lt;/strong&gt; comes in.&lt;/p&gt;
&lt;p&gt;React doesn’t touch the real screen (the DOM) directly. Instead it keeps a copy of the screen’s shape as a lightweight JavaScript object, and when state changes it &lt;strong&gt;compares the new version with the previous one&lt;/strong&gt;, picks out only the parts that actually differ, and applies those to the real DOM. This comparison process is called reconciliation. Thanks to it, we get to write code the easy way — “re-declare the whole thing” — while actual screen updates stay minimal.&lt;/p&gt;
&lt;p&gt;You don’t need to know this deeply right now. Just carry the sense that “React figures out the difference between what I declared and the current screen, and fills the gap itself,” and the coming discussions of State and rendering will follow much more naturally.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-this-series-covers--and-react-native&quot;&gt;What this series covers — and React Native&lt;/h2&gt;
&lt;p&gt;To sum up, React’s idea is three sentences. &lt;strong&gt;Declare the screen as a function of state. Assemble the screen from reusable components. Let React minimize the actual updates.&lt;/strong&gt; In the articles ahead, we’ll lay concepts like JSX, Props, State, useEffect, and Context onto this idea one at a time.&lt;/p&gt;
&lt;p&gt;One thing to flag in advance: Part 1 of this series covers &lt;strong&gt;React on the web&lt;/strong&gt;. React Native, so often mentioned in the same breath, takes the very way of thinking we just described and moves it from the web onto &lt;strong&gt;native mobile screens&lt;/strong&gt; — and we’ll cover it in a separate Part 2. Understand React properly and React Native becomes a story told on top of it, and far easier for it. So for now, let’s start by getting comfortable with this sense of &lt;em&gt;declarative&lt;/em&gt;.&lt;/p&gt;</content:encoded><category>React — Web Development from Start to Deploy</category><category>React</category><category>Declarative UI</category><category>Components</category><category>Frontend</category></item><item><title>On the Risk of a Business Built on Contact Points — When the Founder&apos;s Network Is the Revenue Structure</title><link>https://blog.bepuljang.co.kr/en/startup-prince/03/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/startup-prince/03/</guid><description>The third post in The Startup Prince. We look at why early revenue built on a founder&apos;s personal relationships rarely lasts, borrowing the story of Cesare Borgia — who could not hold power built on borrowed strength.</description><pubDate>Thu, 09 Jul 2026 15:21:30 GMT</pubDate><content:encoded>&lt;p&gt;The previous two posts dealt with vision and money — problems of foundation inside the organization. This time we look outside the organization, at the point where the business actually touches the world, and the deep-rooted risk that can live there.&lt;/p&gt;
&lt;h2 id=&quot;when-the-founders-network-is-the-revenue-structure&quot;&gt;When the Founder’s Network Is the Revenue Structure&lt;/h2&gt;
&lt;p&gt;A startup’s first revenue almost always comes from the founder’s personal relationships. A former colleague, a classmate, someone met somewhere in the industry — connections like these produce the first contract, the second, the tenth. There’s nothing strange about this. Early on, it’s actually the fastest and most efficient way to get going.&lt;/p&gt;
&lt;p&gt;The problem is when this stays the only revenue path even as time passes. The founder is still the starting point of every deal. New customers still only arrive through someone’s introduction. The revenue dashboard is growing, but pull apart the engine behind that growth and you find it standing on a resource that cannot scale: one person’s time and one person’s network.&lt;/p&gt;
&lt;p&gt;This structure rarely surfaces in conversations with investors either. Looking only at the revenue dashboard, the growth trend looks clear enough that nobody feels the need to dig into where it’s actually coming from. The problem only becomes visible when the founder is out for a few weeks — parental leave, a health issue, plain burnout. That’s the moment you can finally see the difference between a company whose new-deal pipeline grinds nearly to a halt, and one that keeps running without much disruption at all.&lt;/p&gt;
&lt;h2 id=&quot;the-fragility-of-power-built-on-borrowed-strength&quot;&gt;The Fragility of Power Built on Borrowed Strength&lt;/h2&gt;
&lt;p&gt;In &lt;em&gt;The Prince&lt;/em&gt;, Machiavelli examines the case of Cesare Borgia in detail. Borgia took the Romagna using strength that wasn’t his own — the backing of his father, Pope Alexander VI, and the French army. Machiavelli credits the way Borgia actually governed once in power. But because the foundation of that power was never fully his own, the moment his father died and his patron disappeared, everything he had worked to build began to unravel almost immediately.&lt;/p&gt;
&lt;p&gt;The lesson Machiavelli draws from this is unambiguous: a position gained through someone else’s strength, someone else’s favor, only lasts as long as that someone else does. Real power has to be built on one’s own capability and resources — what Machiavelli calls one’s own arms.&lt;/p&gt;
&lt;h2 id=&quot;a-contact-point-is-a-starting-line-not-an-engine&quot;&gt;A Contact Point Is a Starting Line, Not an Engine&lt;/h2&gt;
&lt;p&gt;A startup’s relationship-driven revenue carries the exact same fragility. That network is not an infinite resource. The moment it runs dry, or the founder who owns it gets pulled onto something else, the pipeline of new contracts visibly thins out. An even more dangerous version is when the relationships themselves stay bound to the founder personally — so that no matter how large the organization grows, sales still can’t move forward without routing through the founder.&lt;/p&gt;
&lt;p&gt;Plenty of real B2B startups stall out exactly at this stage. For the first few years they build steadily on the founder’s network from a previous job, and only when that network naturally runs dry do they realize there’s no new pipeline behind it. Only after the revenue chart flattens do they start building a sales organization — and by then, a meaningful amount of the time they’d bought has already been lost.&lt;/p&gt;
&lt;h2 id=&quot;not-missing-the-signal-to-make-the-shift&quot;&gt;Not Missing the Signal to Make the Shift&lt;/h2&gt;
&lt;p&gt;This shift usually gets delayed not because there’s no signal, but because the signal is easy to ignore. What percentage of new contracts close without routing through the founder? What share of inbound inquiries that arrive without an introduction actually turn into contracts? Checking these two numbers every quarter is enough on its own to gauge how dependent you are on contact points. If the share of deals not routed through the founder hasn’t moved for several quarters running, the business’s engine is still one person, no matter how the revenue chart trends.&lt;/p&gt;
&lt;p&gt;Organizations that track this early build out a sales team and channels before the founder’s network runs dry. Organizations focused only on the revenue chart only discover there was ever just one engine the moment that growth stops. The later that realization comes, the more time and money it takes to build a replacement.&lt;/p&gt;
&lt;h2 id=&quot;turning-a-contact-point-into-a-system&quot;&gt;Turning a Contact Point Into a System&lt;/h2&gt;
&lt;p&gt;The way to avoid this risk isn’t to eliminate contact points. It’s to look closely at the deals that started there and find whatever repeatable pattern lives inside them. What problem brought these customers to us? What channel did they come through? What message did they actually respond to? Once you have answers, you can move that same pattern onto a structure that doesn’t depend on the founder’s network — content, cold outbound, partnerships, referral programs.&lt;/p&gt;
&lt;p&gt;Teams that pull this off successfully tend to follow a similar process. They list out every deal closed over the past year or two, along with how each one started, how long it took to close, and what message actually landed. Laid out this way, a pattern emerges among what looked like scattered, unconnected wins — customers of a certain industry, size, or problem tend to close unusually fast. Once you know that pattern, you can go find prospects who match it outside the founder’s own network too.&lt;/p&gt;
&lt;p&gt;Without that shift, whatever the revenue dashboard shows, the business is closer to one person’s activity than to a company. A business that stops the moment its founder steps away carries exactly the fragility Machiavelli warned about.&lt;/p&gt;
&lt;p&gt;The next post takes up another kind of borrowed strength: market timing. We look at why growth built on luck tends to collapse the moment that luck runs out.&lt;/p&gt;</content:encoded><category>The Startup Prince</category><category>Startup</category><category>Sales</category><category>Networking</category><category>The Startup Prince</category></item><item><title>How to Put Cognition to Work in Design — and the Quiet Persuasion of Affordance</title><link>https://blog.bepuljang.co.kr/en/uxd-manifesto/07/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/uxd-manifesto/07/</guid><description>We look at what the three cognitive processes of selective attention, association, and mental models mean for actual design, and go on to examine affordance — the concept that guides user behavior through the unconscious.</description><pubDate>Thu, 09 Jul 2026 15:21:30 GMT</pubDate><content:encoded>&lt;p&gt;In the previous post, we looked at three cognitive processes that play a central role in how humans form experience — selective attention, association, and mental models. Each operates at a different stage: selective attention at the entry point where information is received, association in the process of organizing it, and mental models in the stage of interpreting and putting it to use. Now let’s look at what these mean for actual design, and follow that thread through to the concept of affordance, which extends naturally from it.&lt;/p&gt;
&lt;h2 id=&quot;the-challenge-selective-attention-leaves-for-design&quot;&gt;The Challenge Selective Attention Leaves for Design&lt;/h2&gt;
&lt;p&gt;When it comes to selective attention, designers need to consider two dimensions.&lt;/p&gt;
&lt;p&gt;One is quantitative: using visibility and contrast, designers must give core information enough visual distinction — proportional to its priority — that it wins the user’s selective attention. The other is qualitative: designers must deeply understand what the user is interested in and curious about, so that their attention is drawn there naturally.&lt;/p&gt;
&lt;p&gt;What designers should especially guard against here is the complacent assumption that laying out information without much thought, or simply packing in as much of it as possible, will lead users to receive all of it as useful and meaningful. As we saw earlier, human attention is selective, so presenting everything with equal weight amounts to showing nothing properly at all. Information must be rigorously leveled, and the closer a piece of core information sits to the intended experience, the more sharply and compellingly it should be placed in front of the user.&lt;/p&gt;
&lt;h2 id=&quot;association-and-the-hierarchy-of-information&quot;&gt;Association and the Hierarchy of Information&lt;/h2&gt;
&lt;p&gt;Next, association offers an important insight into how information should be organized and layered.&lt;/p&gt;
&lt;p&gt;The information designers want to convey to users must be organized so that it is semantically related, allowing users to naturally chunk it together for processing. Because human short-term processing capacity is limited, information needs to be layered appropriately so that too much of it doesn’t pour in at once. As noted earlier in the discussion of selective attention, information placed without much thought not only fails to catch the user’s eye — from the standpoint of association, it also drives up cognitive load excessively, actively hindering the processing and uptake of information rather than helping it.&lt;/p&gt;
&lt;p&gt;Designers must therefore examine closely both the meaning of each piece of information and how pieces relate to one another. One more thing to watch for here: designers must verify that the relationships they’ve established actually hold from the user’s point of view as well. Information that seems obviously grouped to the designer may be classified by an entirely different logic in the user’s mind. Which pieces of information a user perceives as related can vary considerably depending on that user’s experience and context.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Because the standard of relatedness shifts with the user’s experience and context in this way, association is closely tied to “convenience,” one of the UX design parameters covered elsewhere in this series.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id=&quot;mental-models-and-reducing-the-cost-of-learning&quot;&gt;Mental Models and Reducing the Cost of Learning&lt;/h2&gt;
&lt;p&gt;Finally, of the three cognitive processes, the mental model is the concept most frequently invoked and most highly valued in UX design. The reason is that it serves as an especially powerful and efficient means of guiding the user’s experience.&lt;/p&gt;
&lt;p&gt;In practice, it is close to impossible for the maker of a product or service to explain, one by one, the design intent and how to use every single feature. And even if that were possible, if learning it demands significant time and effort, it becomes a heavy burden for the user — one that can drive them away from the product altogether.&lt;/p&gt;
&lt;p&gt;Minimizing this learning cost is therefore one of the central tasks of UX design, and this is exactly where the mental models users already carry come into their own. When a design makes good use of the informational structures a user has already built up, that user can grasp intuitively how the product works without any separate explanation. This is precisely why a single, well-placed icon can be more useful and more powerful than a lengthy user tutorial, and why it can guide a user’s action naturally and intuitively.&lt;/p&gt;
&lt;h2 id=&quot;new-experience-guided-by-past-experience&quot;&gt;New Experience Guided by Past Experience&lt;/h2&gt;
&lt;p&gt;This quality of naturally drawing out a user’s action through a mental model is called affordance, and the concept deserves a closer look.&lt;/p&gt;
&lt;p&gt;To begin with, affordance is clearly different in character from explicit guidance — a difference already implicit in the term itself. Where guidance tells the user outright, “Press this,” affordance instead leads the user to feel, on their own, “I should press this.”&lt;/p&gt;
&lt;p&gt;Looking at the word’s etymology makes this difference even clearer. The English verb “afford” roughly means “to be worth doing” or “to have the capacity for.” In that spirit, affordance doesn’t force or command — it quietly and naturally leads a person toward an action that is, in a sense, already within their reach.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We noted earlier that “behavior,” unlike conscious “action,” is a representation that manifests unconsciously. That distinction is exactly what affordance draws on — it operates on behavior, not action, which is why it works beneath the level of conscious deliberation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This implies that guidance through a mental model doesn’t arise from a user’s conscious judgment but occurs naturally within the realm of the unconscious. In other words, faced with a design that has good affordance, a user doesn’t consciously wonder, “How do I use this?” — instead, their hand moves in the right direction before they even realize it.&lt;/p&gt;
&lt;p&gt;The major cognitive processes we’ve examined so far — selective attention, association, and mental models — all point in a single direction: they show us how to understand the user, and how to structure a design so that it genuinely improves the user’s experience.&lt;/p&gt;
&lt;p&gt;To summarize: we must first deeply understand where a user’s interest and curiosity lie, and what shape the semantic relationships and mental models they already hold take. Only then can we predict what a user will focus on and what they will ignore, which pieces of information they will perceive as a single chunk, and what functions they will expect from a given form.&lt;/p&gt;
&lt;p&gt;Building on that understanding, we must carefully classify and level information into a clear order of priority, arranging it with an eye not only to the user’s interests but also to their physical and cognitive limits. And ultimately, we should aim for designs that guide behavior naturally through the user’s unconscious rather than through explicit instruction. All of this, in the end, starts from the same place: we must study the user and the information deeply, and design the experience on top of that understanding.&lt;/p&gt;
&lt;p&gt;The next post turns to the outward clue that this cognition leaves behind: “Behavior.” The behavior a user displays is, after all, the thread that lets us read the cognition underneath it.&lt;/p&gt;</content:encoded><category>The UX Design Manifesto</category><category>UX Design</category><category>Cognitive Psychology</category><category>Affordance</category><category>Mental Model</category></item><item><title>Putting Your App Out into the World — A Vibe Coder&apos;s First Steps in Deployment</title><link>https://blog.bepuljang.co.kr/en/vibecoding/08/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/vibecoding/08/</guid><description>Learn how to take an app that only ran locally and deploy it to the internet, step by step from the start.</description><pubDate>Thu, 09 Jul 2026 15:21:00 GMT</pubDate><content:encoded>&lt;p&gt;If you’ve built an app that works well on your own computer, it’s time to put it online so other people can use it too. This process is called &lt;strong&gt;deploying&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Deployment can feel like something only professional developers do, but these days there are plenty of services that let you deploy in minutes with no coding knowledge at all. In this part, we’ll cover what deployment actually means and how to deploy for real.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-is-deployment&quot;&gt;What Is Deployment?&lt;/h2&gt;
&lt;p&gt;Deployment means &lt;strong&gt;taking a service that only ran on your own computer and putting it on the internet so other people can access it too&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The bookmark app we’ve built so far only works at &lt;code&gt;http://localhost:5173&lt;/code&gt;. &lt;code&gt;localhost&lt;/code&gt; is an address that means “my own computer” — it can only be reached from your machine. Once you deploy, you get an address like &lt;code&gt;https://myapp.vercel.app&lt;/code&gt;, and it becomes accessible from anywhere in the world.&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Local development&lt;/th&gt;&lt;th&gt;After deployment&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Address&lt;/td&gt;&lt;td&gt;&lt;code&gt;localhost:5173&lt;/code&gt;&lt;/td&gt;&lt;td&gt;A real URL like &lt;code&gt;myapp.vercel.app&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Who can access it&lt;/td&gt;&lt;td&gt;Only you&lt;/td&gt;&lt;td&gt;Anyone with internet access&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Computer state&lt;/td&gt;&lt;td&gt;Your computer has to be on&lt;/td&gt;&lt;td&gt;A cloud server runs 24/7&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&quot;deploying-the-frontend-5-minutes-with-vercel&quot;&gt;Deploying the Frontend: 5 Minutes with Vercel&lt;/h2&gt;
&lt;p&gt;Apps made of nothing but HTML/CSS/JavaScript, with no backend (server) — static sites, like our bookmark app — can be deployed easily with Vercel.&lt;/p&gt;
&lt;h3 id=&quot;step-1-push-your-code-to-github&quot;&gt;Step 1: Push Your Code to GitHub&lt;/h3&gt;
&lt;p&gt;To deploy, you first need to push your code to GitHub. GitHub is a service for storing and sharing code.&lt;/p&gt;
&lt;p&gt;If you’ve never used Git before, just ask Claude Code for help.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plain&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;I want to push this project to GitHub.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Walk me through it step by step, from initializing git to connecting the GitHub repository.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Claude Code will guide you through it step by step.&lt;/p&gt;
&lt;h3 id=&quot;step-2-sign-up-for-vercel-and-deploy&quot;&gt;Step 2: Sign Up for Vercel and Deploy&lt;/h3&gt;
&lt;p&gt;Log in to &lt;a href=&quot;https://vercel.com&quot;&gt;vercel.com&lt;/a&gt; with your GitHub account.&lt;/p&gt;
&lt;p&gt;Click the “New Project” button and select the GitHub repository you just created. Vercel will automatically detect that it’s a React/Vite project and suggest build settings. Click “Deploy,” and the deployment finishes in one or two minutes.&lt;/p&gt;
&lt;p&gt;Once it’s done, you’ll get an address in the form of &lt;code&gt;https://your-project-name.vercel.app&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;automatic-redeployment&quot;&gt;Automatic Redeployment&lt;/h3&gt;
&lt;p&gt;The great thing about Vercel is that from then on, every time you push code to GitHub, it automatically redeploys. You don’t need to do anything extra — just edit your code, push it to GitHub, and your changes go live a few minutes later.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;comparing-different-deployment-services&quot;&gt;Comparing Different Deployment Services&lt;/h2&gt;
&lt;p&gt;Depending on your situation, different deployment services make sense.&lt;/p&gt;









































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Service&lt;/th&gt;&lt;th&gt;Features&lt;/th&gt;&lt;th&gt;Best for&lt;/th&gt;&lt;th&gt;Pricing&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Vercel&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Optimized for React/Next.js, easy GitHub integration&lt;/td&gt;&lt;td&gt;Frontend-focused projects&lt;/td&gt;&lt;td&gt;Free plan available&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Netlify&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Similar to Vercel, with extras like form handling&lt;/td&gt;&lt;td&gt;Static sites, or when you need a simple backend&lt;/td&gt;&lt;td&gt;Free plan available&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Render&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Can deploy backends too (Node.js, Python, etc.)&lt;/td&gt;&lt;td&gt;Full-stack apps&lt;/td&gt;&lt;td&gt;Free plan available (slower)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Railway&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Docker-based deployment, includes databases&lt;/td&gt;&lt;td&gt;Complex backends, when you need a DB&lt;/td&gt;&lt;td&gt;Starts at $5/month&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;AWS / GCP&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Professional cloud services, highly scalable&lt;/td&gt;&lt;td&gt;Large-scale services, fine-grained control&lt;/td&gt;&lt;td&gt;Usage-based pricing&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;If this is your first time deploying anything, starting with Vercel or Netlify is a good choice.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;things-to-check-after-deployment&quot;&gt;Things to Check After Deployment&lt;/h2&gt;
&lt;p&gt;Once deployment is done, there are a few things worth checking.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Check that everything works&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Visit the deployed address and make sure all the features that worked locally still work the same way. It’s fairly common for something to work fine locally but break in the deployed environment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Check for errors&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You can check build logs and function logs from the Vercel dashboard. If there’s an error, paste the log into Claude Code and ask for help.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-your-first-deployment-means&quot;&gt;What Your First Deployment Means&lt;/h2&gt;
&lt;p&gt;The app that used to only run locally is now live on the internet. Share the address, and anyone can access it. This is the moment your service actually “comes out into the world.”&lt;/p&gt;
&lt;p&gt;In the next part, we’ll cover something you should always do after deploying: refactoring. Building quickly tends to make code messy, so we’ll look at how to read through and clean up your code together with AI.&lt;/p&gt;</content:encoded><category>Vibe Coding for Everyone</category><category>Deployment</category><category>Vercel</category><category>GitHub</category><category>Vibe Coding</category></item><item><title>Speculative Design: Designing the Preferred Future</title><link>https://blog.bepuljang.co.kr/en/speculative-design/01/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/speculative-design/01/</guid><description>More than forecasting the future, speculative design proposes a better path for society as a whole. A look at the concept and its design direction.</description><pubDate>Thu, 09 Jul 2026 10:51:08 GMT</pubDate><content:encoded>&lt;p&gt;There is a concept called Speculative Design. “Design” is generally thought to have begun as craft — and from the craftsman’s idea of adding art to a manufactured good, it gradually branched into thinking about art within the very process of making that good. As its meaning expanded to mean planning and orchestrating a process around its purpose and efficiency, the word “speculation” — meaning conjecture, a looking-ahead — was attached to it.&lt;/p&gt;
&lt;p&gt;Read literally, it means design that predicts and envisions the future. And in truth, that is natural to the very idea of “design.” Design has always predicted the users and use-environment of its object and offered a form optimized for the purpose at hand. Taken at face value, all design already contains prediction and foresight.&lt;/p&gt;
&lt;p&gt;That is exactly why I’m curious about the background against which this particular term arose, and what distinctive concept it is meant to describe. Speculative design is characterized by taking a more social and macroscopic view — one that goes beyond mere prediction within the process of some design object.&lt;/p&gt;
&lt;p&gt;Put differently: where earlier design deliberated over a specific object such as a product, a service, or a company, and — through its anticipatory reading of that object’s future — proposed the optimal form for a goal, speculative design takes society and the age as a whole as its design object, and imagines and proposes, within the technology and environment given to us, a better path for our society to move toward.&lt;/p&gt;
&lt;p&gt;The Futures Cone (also called the cone of possibility), often discussed in speculative design, is a good diagram for illustrating this visually. Taking the whole range of futures that could arise from our present, it sorts them by likelihood into probable, plausible, and possible futures — and shows that among these, we can propose moving toward a preferred future.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;A cone that widens over time from the present. From the inside out it is divided into probable, plausible and possible futures, and the preferred future is marked as a point inside the cone.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1400&quot; height=&quot;780&quot; src=&quot;https://blog.bepuljang.co.kr/_astro/futures-cone._KvveoYZ_1MHr92.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;small&gt;The futures cone, redrawn after Hancock &amp;#x26; Bezold (1994) and Joseph Voros&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;There has long been a related field called conceptual design, which similarly presents objects of the imagination. But what sets speculative design apart is this: where conceptual design ultimately keeps its emphasis on reality and stops at presenting the usability of new products and services in the near future (Probability), speculative design has a leading edge — it presents and guides a vision of the future and the path we ought to take (Preferred). That is, its key distinction is that it designs not the future as a result of the present, but an improved future that we can reach through change.&lt;/p&gt;
&lt;p&gt;We usually survey the future as a dark, forbidding dystopia. This is likely because we tend to feel a loss more acutely than an equal gain (loss aversion, from prospect theory) and to react more strongly to negative stimuli. But design has always overcome that human nature, finding the most optimal outcome within given conditions and goals.&lt;/p&gt;
&lt;p&gt;Speculative design, too, finds its meaning in looking coldly at the conditions we have and, within them, seeking the path society should take and the future that would be most preferred. It’s about finding, in the utopia given to us, its first thread. The single most ideal future.&lt;/p&gt;
&lt;p&gt;In this series, I’ll look in turn at where this concept came from, what work it has produced, and what criticism it has drawn. At the end, I’ll come back to that last sentence as well.&lt;/p&gt;</content:encoded><category>What is Speculative Design</category><category>Speculative Design</category><category>Design</category><category>UX Design</category><category>Future</category></item><item><title>On the Risk of Making Money the Goal — The Paradox of Chasing It Further Away</title><link>https://blog.bepuljang.co.kr/en/startup-prince/02/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/startup-prince/02/</guid><description>The second post in The Startup Prince. Even when a vision exists, money can quietly slide into its seat. We look at how that shift distorts an organization&apos;s decisions, borrowing Machiavelli&apos;s view of wealth as a byproduct rather than a goal.</description><pubDate>Wed, 08 Jul 2026 15:06:46 GMT</pubDate><content:encoded>&lt;p&gt;The previous post looked at the risk of an organization scattering in different directions when its vision is never made explicit. But having a vision doesn’t make you safe either. What happens more often is that the vision is genuinely there, and money still quietly slides into the seat it was supposed to occupy.&lt;/p&gt;
&lt;h2 id=&quot;the-paradox-of-chasing-it-further-away&quot;&gt;The Paradox of Chasing It Further Away&lt;/h2&gt;
&lt;p&gt;An organization that makes money its goal actually moves fast at first. This month’s revenue target, this quarter’s net new revenue, a conversion rate expressed as a percentage — numeric goals are clear and measurable, so it’s easy for everyone to align around them. You run promotions, adjust pricing, push upsells harder. And the numbers really do go up.&lt;/p&gt;
&lt;p&gt;The trouble comes after. Users who’ve grown used to promotions stop responding to full price. Upsells pushed too aggressively drag the churn rate up right alongside them. You’ve essentially borrowed against next month’s trust to fill this month’s revenue line. Six months, a year later, you find yourself hitting the revenue target every single time while the company’s underlying health keeps getting weaker. You chased the money, and it kept getting further away.&lt;/p&gt;
&lt;p&gt;The pattern comes through more clearly in a concrete case. One subscription startup, trying to hit its quarterly revenue target, kept issuing steep discount codes to new signups every month. New revenue beat the target every time, but a structure quietly took hold underneath it: nobody renewed without a discount. A few quarters later, when the discounts were pulled, revenue was cut in half almost overnight — completely disconnected from that streak of target-beating months.&lt;/p&gt;
&lt;h2 id=&quot;machiavellis-view-wealth-as-a-byproduct-not-a-goal&quot;&gt;Machiavelli’s View: Wealth as a Byproduct, Not a Goal&lt;/h2&gt;
&lt;p&gt;In &lt;em&gt;The Prince&lt;/em&gt;, Machiavelli warns against a ruler squeezing wealth out of his people too aggressively. Heavy taxation born of a thin treasury breeds resentment, and resentment is the seed of rebellion. The healthy treasury he does endorse is one that accumulates naturally as a byproduct of protecting the people and stabilizing the state. Wealth was never the prince’s goal in itself — it was the means, and the result, of some other goal: stability and defense.&lt;/p&gt;
&lt;p&gt;The same distinction applies directly to startups. Revenue can’t be the goal. Revenue is the result of a user feeling that a real problem got solved, and paying for it. Reverse that order — aim directly at the result called revenue — and you start making decisions that neglect or damage the very thing that produces that result: the quality of the problem you’re solving.&lt;/p&gt;
&lt;h2 id=&quot;what-gets-distorted&quot;&gt;What Gets Distorted&lt;/h2&gt;
&lt;p&gt;The first thing to warp when money becomes the goal is priority. Fixing the small but fundamental friction users run into constantly loses out, every time, to whatever ships revenue right now — an extra checkout step, a pricing overhaul, a promo banner. Any single one of these calls isn’t a big deal. Repeated often enough, the product slowly tilts away from the user and toward the revenue chart.&lt;/p&gt;
&lt;p&gt;There’s a distortion especially common among startups preparing to raise their next round. To make the metrics look good for that round, a business model with genuinely weak retention gets dressed up using revenue numbers alone. The chart you show investors trends up and to the right, but underneath it is a structure that’s simply refilling churned users with new acquisition, over and over. That structure always surfaces eventually — usually at the exact moment the company is least equipped to handle it.&lt;/p&gt;
&lt;p&gt;The same distortion shows up at the customer-facing edge too. To hit this quarter’s numbers, it’s common to cut support staff or tighten the refund policy. Costs drop and the P&amp;#x26;L looks better right away, but customers who walk away disappointed from that decision quietly leave at their next renewal instead. Churn is a cost that’s invisible at the moment the decision gets made, so what actually got sacrificed only shows up much later.&lt;/p&gt;
&lt;h2 id=&quot;proxy-metrics-and-real-metrics&quot;&gt;Proxy Metrics and Real Metrics&lt;/h2&gt;
&lt;p&gt;Avoiding this distortion requires being clear about what to watch instead of revenue. Since revenue is the result, you need to track the metrics on the cause side that actually produce it — things like how often a user comes back after a specific problem gets solved, how much repeat purchase happens without any prompting, how long it takes a new user to reach the core value on their first use. If revenue is climbing while these cause-side metrics stay flat, that’s a signal the revenue growth won’t last.&lt;/p&gt;
&lt;p&gt;Healthy organizations set revenue targets by first defining which cause-side improvement that revenue is supposed to come from. Not “grow revenue 20% this quarter,” but “raise the reuse rate of our core feature by 15 points, and let revenue growth follow from that.” Once that order is made explicit, it also becomes obvious what to check when revenue doesn’t move.&lt;/p&gt;
&lt;h2 id=&quot;money-follows&quot;&gt;Money Follows&lt;/h2&gt;
&lt;p&gt;The conclusion is simple. The goal always has to be “what problem are we solving,” and money has to be the result that follows when that’s done well. An organization that keeps this order intact knows exactly what to fix when revenue wobbles — go back to the problem. An organization that has made money the goal tends to respond to the same wobble with a harder promotion, a more aggressive upsell. The same prescription that grew the disease in the first place.&lt;/p&gt;
&lt;p&gt;This is exactly where the vision from the previous post matters again. If “the problem we solve” is concretely inscribed across the whole organization, you have a standard to notice — and reverse — the moment revenue tries to quietly take the goal’s seat.&lt;/p&gt;
&lt;p&gt;The next post takes up another kind of false foundation: a business run entirely on one person’s relationships, where the founder’s network has become the whole revenue structure.&lt;/p&gt;</content:encoded><category>The Startup Prince</category><category>Startup</category><category>Revenue</category><category>Growth</category><category>The Startup Prince</category></item><item><title>Inside the User&apos;s Mind, the Iceberg of Cognition — Selective Attention, Chunking, and Mental Models</title><link>https://blog.bepuljang.co.kr/en/uxd-manifesto/06/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/uxd-manifesto/06/</guid><description>User experience is ultimately completed inside the user&apos;s mind. Using the iceberg as a metaphor for cognition, we examine three cognitive processes at work beneath the surface: Selective Attention, Chunking, and Mental Model.</description><pubDate>Wed, 08 Jul 2026 15:06:46 GMT</pubDate><content:encoded>&lt;p&gt;Cognition refers to the overall mental process by which we take in and process external stimuli and information. It is not confined to mere sensory perception — seeing with the eyes, hearing with the ears — but encompasses the entire sequence of interpreting, categorizing, remembering, and later retrieving and using the stimuli we have taken in.&lt;/p&gt;
&lt;p&gt;When we encounter an object or environment in the world, we gather information through every sensory representation we can perceive from it. Some of this we unconsciously filter out, while some we selectively accept and construct into meaning of our own. What matters here is that the sum of the meaning formed this way about a given object becomes, in itself, the user experience of that object. This is precisely why people using the very same product can end up with different experiences, and why understanding cognition in the field of UX design is not mere background knowledge but a fundamental starting point for design itself.&lt;/p&gt;
&lt;h2 id=&quot;the-iceberg-of-cognition&quot;&gt;The Iceberg of Cognition&lt;/h2&gt;
&lt;p&gt;Human cognition is often compared to an iceberg.&lt;/p&gt;
&lt;p&gt;It’s a well-worn metaphor, but whenever something enormous lies hidden beneath the surface, we like to call it “the tip of the iceberg.” The same holds true for human cognition, and this iceberg metaphor offers a genuinely useful frame for understanding its nature. Just as the portion of an iceberg that rises above the water is only a small fraction of the whole, the visible realm of conscious awareness occupies a smaller share of cognition overall than we tend to assume. It is the subconscious hidden beneath the surface that is far larger and more extensive, and it exerts a profound influence on our judgments and reactions.&lt;/p&gt;
&lt;p&gt;And just as observing only the iceberg above the waterline makes it hard to gauge precisely what lies beneath, observing only a user’s conscious responses makes it difficult to grasp the subconscious mechanisms operating behind them — which is exactly why this metaphor captures the nature of cognition so well.&lt;/p&gt;
&lt;p&gt;Taking the analogy further: just as the Titanic met disaster by colliding with the hidden mass beneath the water’s surface, user experience, too, can run into unexpected consequences by colliding with subconscious cognitive patterns that the designer failed to grasp. A design that appears, on its face, to have no problems at all can still cause users discomfort or confusion — and this is precisely the context in which that happens.&lt;/p&gt;
&lt;p&gt;This is why, just as navigators use sonar and underwater surveys to map the hidden face of an iceberg, UX designers, too, need to make the effort to understand the full shape of this iceberg of cognition. Through user research, behavioral analysis, psychological theory, and other means, we need to study what cognitive processes shape the experience of our target users, and at which point in that process — and in what way — we should intervene and adjust it to create a better experience.&lt;/p&gt;
&lt;p&gt;Of course, diving deep into the full depths of cognitive engineering would be an overwhelming and difficult undertaking, but that is no reason to give up on understanding cognition altogether. Let’s begin, then, by looking at a few of its core mechanisms.&lt;/p&gt;
&lt;h2 id=&quot;three-cognitive-processes-that-shape-experience&quot;&gt;Three Cognitive Processes That Shape Experience&lt;/h2&gt;
&lt;p&gt;If we view the process as a cycle — an external object is perceived through the sense organs, that perception is stored as memory, and that memory in turn shapes how new information is processed — three major cognitive processes stand out within this cycle: Selective Attention, Chunking, and Mental Model.&lt;/p&gt;
&lt;p&gt;Each of these three plays a central role at a different stage: one at the gateway where information enters, one in the process of organizing that information, and one in the stage of interpreting and putting that information to use. Let’s look at each in turn.&lt;/p&gt;
&lt;h3 id=&quot;selective-attention&quot;&gt;Selective Attention&lt;/h3&gt;
&lt;p&gt;The first gateway of cognition is Selective Attention. Unlike a camera or a recorder, we cannot take in every last piece of information that reaches our sense organs.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In reality, even a camera or a recorder registers different amounts of information at the center of the frame versus the periphery. But this is simply a physical limitation caused by distance, whereas human selective attention goes further than the physical realm, actively discarding information that is deemed cognitively unnecessary.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Because our cognitive resources are limited, when we focus attention on a particular area, we take in the densest information from that area, while our receptivity to information outside that area of focus drops off sharply. It’s much like shining a flashlight into a dark room: whatever the light touches is seen clearly, while everything else remains in darkness.&lt;/p&gt;
&lt;p&gt;Various factors drive what we choose to focus on — our own preferences and interests, the level of risk or urgency carried by the information itself, and the visual contrast it has against surrounding information, among others. In general, the higher an element’s visibility, the more likely it is to be selected for attention, though this is not always the case. Because this selection is deeply shaped by the memories and experiences a person already carries, even when looking at the very same screen, users can differ considerably in where their attention lands and what they notice first.&lt;/p&gt;
&lt;h3 id=&quot;chunking&quot;&gt;Chunking&lt;/h3&gt;
&lt;p&gt;Information that has made it through the filter of selective attention isn’t home free yet. At the next stage, our cognition performs another crucial operation: grouping the information we’ve received by meaning, so as to reduce the number of items we have to process.&lt;/p&gt;
&lt;p&gt;One well-known “law” related to this is the Magic Number 7 — the idea that the amount of information a person can process in short-term memory is around seven, or seven plus or minus two. Follow-up research has raised plenty of disagreement about the exact figure, with some arguing the real number is even smaller, but what truly matters here isn’t the specific number — it’s the underlying fact that human short-term information processing has a clear limit.&lt;/p&gt;
&lt;p&gt;And the strategy our cognition naturally uses to work around this limit is Chunking. Taking in and processing a set of unrelated, individual pieces of information one by one demands an enormous cognitive workload — or may be impossible altogether — but when information that is meaningfully related is grouped into a single chunk, the cognitive load drops sharply, and as a result we become able to process a far larger volume of information efficiently.&lt;/p&gt;
&lt;h3 id=&quot;mental-model&quot;&gt;Mental Model&lt;/h3&gt;
&lt;p&gt;If chunking is the process of efficiently grouping incoming information, a Mental Model goes a step further: it organizes that information into a coherent, systematic structure. Drawing on accumulated memory and experience, we build structures of information about particular objects or situations, and when we later encounter new information, we use these structures as a frame through which to receive and interpret it.&lt;/p&gt;
&lt;p&gt;Take, for example, the object we call a camera lens — each of us carries our own structure of information about it. Someone with little experience with cameras might have a structure that goes no further than “round in shape” and “the basic function of projecting an image.” Someone who has spent years taking photographs, on the other hand, will have a far richer structure that includes the workings of the aperture, its relationship to shutter speed, the effects of various filters, and other fine-grained details.&lt;/p&gt;
&lt;p&gt;The same is true for something like a gun. One person’s structure might contain only basic elements like the trigger and the grip, while someone with more experience might have it systematically organized down to details like the sights, the safety, and the magazine.&lt;/p&gt;
&lt;p&gt;Now imagine someone who holds their own structure of information about both lenses and guns encountering an unfamiliar object that seems to combine the shapes of the two. What happens? Even though it’s an object they’ve never seen or used before, their existing structures for lenses and for guns activate automatically, and they can gauge with surprising accuracy how the strange object works and what it does. They will likely connect the trigger-pulling motion suggested by the gun-like shape with the photo-taking function suggested by the lens-like shape, and expect that pulling the trigger will take a picture of whatever it’s aimed at.&lt;/p&gt;
&lt;p&gt;The reason such an inference is possible for an object never encountered before is that our cognition actively draws on structures of information built from past experience. That structure of information is precisely what we call a Mental Model.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;The next post looks at what concrete implications these three cognitive processes — Selective Attention, Chunking, and Mental Model — have for actual design, and explores affordance, a concept closely tied to the Mental Model.&lt;/p&gt;</content:encoded><category>The UX Design Manifesto</category><category>UX Design</category><category>Cognitive Psychology</category><category>Mental Model</category><category>User Experience</category></item><item><title>Prompt Engineering — Getting Exactly What You Want from the AI</title><link>https://blog.bepuljang.co.kr/en/vibecoding/07/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/vibecoding/07/</guid><description>Practical skills for writing good prompts — specificity, context, and scope, plus five techniques for getting the result you&apos;re after.</description><pubDate>Wed, 08 Jul 2026 15:06:46 GMT</pubDate><content:encoded>&lt;p&gt;In part 4, we turned an idea into a prompt and built our first screen. Keep going with vibe coding and you quickly realize one thing: &lt;strong&gt;the quality of the result comes down to how you ask.&lt;/strong&gt; With the same tool and the same AI, the outcome changes dramatically depending on how you write your prompt.&lt;/p&gt;
&lt;p&gt;This post covers the practical skill of writing good prompts — what’s often called &lt;strong&gt;prompt engineering&lt;/strong&gt;. The name sounds grand, but the core is simple: it’s no different from &lt;strong&gt;delegating a task well to a capable colleague.&lt;/strong&gt; We’ll keep using the bookmark app as our example.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;three-conditions-for-a-good-prompt&quot;&gt;Three Conditions for a Good Prompt&lt;/h2&gt;
&lt;p&gt;Three things separate a good prompt from a bad one: &lt;strong&gt;specificity, context, and scope.&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Specificity&lt;/strong&gt; — spell out what you want, and in what form.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Context&lt;/strong&gt; — tell the AI what state the project is currently in.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scope&lt;/strong&gt; — decide how far to go in a single request.&lt;/li&gt;
&lt;/ul&gt;




















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Type&lt;/th&gt;&lt;th&gt;Example Prompt&lt;/th&gt;&lt;th&gt;Result&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Bad prompt&lt;/td&gt;&lt;td&gt;”Add some features to the bookmark app”&lt;/td&gt;&lt;td&gt;The AI has no idea what feature you mean or what it should look like&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Good prompt&lt;/td&gt;&lt;td&gt;See the techniques below&lt;/td&gt;&lt;td&gt;Clear on what, in what context, and how far&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Let’s look at five techniques for turning these three conditions into actual prompts.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;technique-1-one-thing-at-a-time&quot;&gt;Technique 1. One Thing at a Time&lt;/h2&gt;
&lt;p&gt;The first principle to follow is &lt;strong&gt;one thing at a time.&lt;/strong&gt; Without experience, you’ll want to ask for everything at once — “add tags and search and sorting.” But that approach is a recipe for trouble.&lt;/p&gt;




















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Approach&lt;/th&gt;&lt;th&gt;Looks like&lt;/th&gt;&lt;th&gt;Actually&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Many features at once&lt;/td&gt;&lt;td&gt;Seems fast&lt;/td&gt;&lt;td&gt;Code gets tangled and hard to change later; when an error hits, the cause is hard to find&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;One at a time&lt;/td&gt;&lt;td&gt;Seems slow&lt;/td&gt;&lt;td&gt;You can check the result at each step, and when something breaks, the cause is clear&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Asking one at a time is actually &lt;strong&gt;faster overall.&lt;/strong&gt; You can see exactly where a bug came from, which cuts the time to fix it dramatically.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;technique-2-give-structured-context&quot;&gt;Technique 2. Give Structured Context&lt;/h2&gt;
&lt;p&gt;The AI doesn’t automatically know the full state of your project, so you have to hand it context along with the request. Splitting it into four parts works well.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[Current state]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;The bookmark app currently saves a URL and title and shows them as a list.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Data is stored in localStorage, and each bookmark has the shape { id, url, title }.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[Feature to add]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;I want to add tags. When saving a bookmark, I should be able to attach several tags,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;and clicking a tag in the list should filter down to just that tag&apos;s bookmarks.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[Data structure change]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Add a tags: [] array to the existing bookmark data.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;For existing data, treat a missing tags field as an empty array.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[Scope limit]&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Just do this much for now. Tag editing and deletion will come later.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Writing it in the order &lt;strong&gt;current state → what you want → data change → scope limit&lt;/strong&gt; greatly raises the odds that the AI builds exactly what you want without breaking existing code. If you’ve organized your project structure in &lt;code&gt;CLAUDE.md&lt;/code&gt; (part 3), you can replace “current state” with a single line: “refer to CLAUDE.md.”&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;technique-3-show-the-shape-you-want-with-an-example&quot;&gt;Technique 3. Show the Shape You Want with an Example&lt;/h2&gt;
&lt;p&gt;For anything hard to put into words, &lt;strong&gt;one example beats a hundred.&lt;/strong&gt; When the output format or style is already clear in your head, show it instead of describing it.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Change the delete button to look like this:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Position: top-right corner of the card&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;- Look: a gray × icon that turns red on hover&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;To pin down an output format: say “organize the result as a table like this” and add one example row.&lt;/li&gt;
&lt;li&gt;To match something that already exists: point at a reference, like “make it in the same style as the BookmarkCard component.”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The AI follows a &lt;strong&gt;concrete model&lt;/strong&gt; far better than an abstract instruction.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;technique-4-let-it-ask-when-it-doesnt-know&quot;&gt;Technique 4. Let It Ask When It Doesn’t Know&lt;/h2&gt;
&lt;p&gt;You don’t have to nail down every detail before you fire off a request. In fact, it’s better to &lt;strong&gt;let the AI ask&lt;/strong&gt; about the fuzzy parts — deciding together, just like collaborating with a person.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;I want to add a login feature, but I haven&apos;t decided on the method (email/social) or where to store data.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Lay out the options with pros and cons first, and ask me about anything that needs a decision.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Add the line “ask me first if anything is unclear,” and the AI throws confirming questions instead of deciding on its own and barreling ahead. That keeps code from piling up on a wrong assumption.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;technique-5-if-its-off-stop-and-reset-the-direction&quot;&gt;Technique 5. If It’s Off, Stop and Reset the Direction&lt;/h2&gt;
&lt;p&gt;Once the AI starts heading the wrong way, don’t watch it play out to the end — &lt;strong&gt;stop it right away.&lt;/strong&gt; The more bad code piles up, the harder it is to undo.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Stop it, point out what went off, and give a correcting prompt.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;That last direction wasn&apos;t what I meant. I wanted X, but you went with Y.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Redo it based on X.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;If the conversation has grown so long that the context has frayed, resetting with &lt;code&gt;/clear&lt;/code&gt; and starting fresh from your CLAUDE.md is also an option.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;wrapping-up-a-good-prompt-is-good-delegation&quot;&gt;Wrapping Up: A Good Prompt Is Good Delegation&lt;/h2&gt;
&lt;p&gt;“Prompt engineering” sounds like some special technique, but in the end it’s &lt;strong&gt;how to delegate a task well.&lt;/strong&gt; Think of it as asking a capable colleague for help: be specific about what you want, hand over the context of where things stand, ask for only as much as fits in one go, and leave room to ask back when something’s unclear.&lt;/p&gt;
&lt;p&gt;Once this instinct settles into your hands, the same AI gives you far better results. The prompt is the tool you’ll use longest in vibe coding — and the one where skill makes the biggest difference.&lt;/p&gt;
&lt;p&gt;In the next post, we cover how to put an app that only ran locally out onto the internet — deployment.&lt;/p&gt;</content:encoded><category>Vibe Coding for Everyone</category><category>vibe coding</category><category>Claude Code</category><category>prompting</category><category>prompt engineering</category></item><item><title>On the Risk of a Vision Never Made Explicit</title><link>https://blog.bepuljang.co.kr/en/startup-prince/01/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/startup-prince/01/</guid><description>The first post in a series that reads Machiavelli&apos;s The Prince as a guide to startups. We look at how a vision that exists but is never made explicit quietly scatters an organization in different directions.</description><pubDate>Tue, 07 Jul 2026 16:52:33 GMT</pubDate><content:encoded>&lt;p&gt;In &lt;em&gt;The Prince&lt;/em&gt;, Machiavelli laid out how a ruler gains and holds power. The princes he observed acquired their territories through war, through marriage, sometimes through sheer luck. Yet few of them held onto those territories for long. Acquiring power and keeping it turned out to be two entirely different problems.&lt;/p&gt;
&lt;p&gt;Startups are no different. Starting a company on the strength of a single idea, or a single well-timed moment, is relatively easy. What’s hard is growing that into an organization that lasts. This series, &lt;em&gt;The Startup Prince&lt;/em&gt;, is about the many ways a venture falls apart in that gap — things that look like growth but rest on unstable foundations, misunderstandings in how a leader leads, and the moments when a founder fails to protect what needed protecting. We’ll work through them one by one, borrowing the frame of &lt;em&gt;The Prince&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The first risk we’ll cover is the most common one, and the one that surfaces the latest. It isn’t the absence of a vision. It’s a vision that exists but was never made explicit.&lt;/p&gt;
&lt;h2 id=&quot;everyones-working-hard-so-why-are-they-facing-different-directions&quot;&gt;Everyone’s Working Hard, So Why Are They Facing Different Directions?&lt;/h2&gt;
&lt;p&gt;From the outside, nothing looks wrong with this organization. The engineering team is shipping features late into the night. Sales is booking new meetings every day. Marketing runs a new campaign every week. No one is being lazy. If anything, everyone is working too hard.&lt;/p&gt;
&lt;p&gt;The problem is the direction of all that effort. Engineering is optimizing for shipping as many features as possible before the next quarter. Sales is optimizing for the number of contracts signed. Marketing is optimizing for traffic and sign-ups. Each team, from its own vantage point, believes it is doing its best for the company. But six months later, you find yourself with more features than before, and the core problem your key users actually cared about is still unsolved; more contracts than before, and churn rising right alongside them; more sign-ups than before, with active usage flat.&lt;/p&gt;
&lt;p&gt;No one did anything wrong. Each team faithfully pursued the goal it was given. The problem is that those goals never pointed in the same direction to begin with.&lt;/p&gt;
&lt;h2 id=&quot;when-the-princes-aims-are-unclear-vassals-each-interpret-them-their-own-way&quot;&gt;When the Prince’s Aims Are Unclear, Vassals Each Interpret Them Their Own Way&lt;/h2&gt;
&lt;p&gt;One thing Machiavelli returns to again and again in &lt;em&gt;The Prince&lt;/em&gt; is that a ruler’s intentions must be made unmistakably clear to those who serve under him. If it isn’t clear whether the prince wants territorial expansion, stable rule, or the goodwill of his people, his vassals begin filling that vacuum with their own judgment. One raises taxes aggressively to fund conquest; another delays necessary reforms in the name of stability. The prince believes he is governing one country. In practice, several different countries are quietly being run under his name.&lt;/p&gt;
&lt;p&gt;A startup founder stands in exactly the same position. There is a clear picture in the founder’s head. It simply hasn’t been written down, spoken consistently, or repeated often enough to take root across the organization. The founder believes the vision has already been communicated — and it may well have been, more than once. But whether that vision actually functions as a concrete standard in each team’s daily decisions is an entirely different question.&lt;/p&gt;
&lt;h2 id=&quot;a-vision-is-not-a-manifesto--its-a-decision-making-standard&quot;&gt;A Vision Is Not a Manifesto — It’s a Decision-Making Standard&lt;/h2&gt;
&lt;p&gt;A common misunderstanding shows up here. If you distill the vision into one polished sentence and put it on the wall, or on the first page of the pitch deck, it’s easy to assume the job of “communicating the vision” is done. Lines like “making the world more convenient” or “finance for everyone” sound good and read smoothly in front of investors. But sentences like these don’t answer any concrete question that comes up on the ground.&lt;/p&gt;
&lt;p&gt;A real vision is tested by questions like these. This quarter, should we put more resources into new features or into stabilizing what already exists? Revenue is growing, but it’s coming from a customer segment outside the core users we defined — do we keep chasing it, or let it go? If a moment comes where protecting the team’s culture means slowing growth, which do we choose? If people across the organization arrive at similar answers to these questions, the vision is actually getting through. If different departments give completely different answers to the same question, then no matter how polished the sentence on the wall is, the vision isn’t yet alive inside the organization.&lt;/p&gt;
&lt;h2 id=&quot;the-illusion-of-implicit-agreement&quot;&gt;The Illusion of Implicit Agreement&lt;/h2&gt;
&lt;p&gt;A trap that shows up especially often in early-stage startups is believing that because everyone agreed on “fast growth,” the vision has already been shared. But “fast growth” specifies nothing on its own. Whether it means growth in revenue, growth in users, or growth in brand awareness determines a completely different metric for each team to optimize.&lt;/p&gt;
&lt;p&gt;In the early days, when the team is small, this ambiguity rarely surfaces. The founder is involved in nearly every decision directly, so any drift in direction gets corrected immediately, by the founder alone. The problem starts the moment the organization grows large enough that each team begins making decisions independently. From that point on, it’s no longer the picture in the founder’s head but what each team actually understands the goal to be that determines the organization’s direction. And if the vision still hasn’t been put into words by then, teams start chasing different metrics, and the organization quietly scatters in several directions at once.&lt;/p&gt;
&lt;p&gt;This scattering is hard to see. Each team’s individual performance report still looks positive on its own. The problem only becomes unmistakable much later — the moment you add up all those individually good numbers and realize the company hasn’t actually moved any closer to where it originally set out to go.&lt;/p&gt;
&lt;h2 id=&quot;what-it-means-to-put-a-vision-into-writing&quot;&gt;What It Means to Put a Vision Into Writing&lt;/h2&gt;
&lt;p&gt;Making a vision explicit isn’t the work of crafting a catchy slogan. It’s the work of setting concrete priorities and standards that will sit in front of every decision the organization makes, and repeatedly checking that they hold up. What’s the priority this quarter? Given that priority, which of the choices in front of us right now should we make? And are the choices different teams have each made on their own actually pointing in the same direction? A founder has to keep asking these questions.&lt;/p&gt;
&lt;p&gt;This isn’t something you finish with a single announcement. It’s work that has to repeat for as long as the organization keeps growing. When a prince fails to make his aims clear to his vassals, they each do their best to build a different country. When a founder fails to keep re-inscribing the vision concretely into the organization, every team does its best to build a different company.&lt;/p&gt;
&lt;p&gt;The next post takes up a risk that pairs with this one: the difficulties a business runs into when a misunderstanding of vision leads it to make money the goal.&lt;/p&gt;</content:encoded><category>The Startup Prince</category><category>Startup</category><category>Entrepreneurship</category><category>Vision</category><category>The Startup Prince</category></item><item><title>Usability, Conveniency, Pleasurability — The Three Parameters UX Design Actually Works With</title><link>https://blog.bepuljang.co.kr/en/uxd-manifesto/05/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/uxd-manifesto/05/</guid><description>We define the three parameters UX design can actually adjust — usability, conveniency, and pleasurability — and examine the trade-offs between them through concrete examples.</description><pubDate>Tue, 07 Jul 2026 15:45:16 GMT</pubDate><content:encoded>&lt;p&gt;In the previous post, we saw that the UX pyramid is made up of six attributes that together form the user experience — functionality, reliability, usability, conveniency, pleasurability, and meaningfulness — and that the area UX design can actually work with is the three in the middle tier: usability, conveniency, and pleasurability. Now let’s take a closer look at each of these three parameters.&lt;/p&gt;
&lt;h2 id=&quot;the-three-parameters-that-design-the-user-experience&quot;&gt;The Three Parameters That Design the User Experience&lt;/h2&gt;
&lt;p&gt;First, usability. As mentioned earlier, usability is classified as an objective attribute that can be quantified. What usability measures is the number of steps a task requires and their complexity — the cognitive procedure a user has to go through to accomplish a given function. More specifically, it looks at how many steps (depth) or how many tasks are involved in the cognitive process of performing a function, and whether the cognitive resources invested are proportionate to the utility that function provides. Thanks to these characteristics, usability has the advantage of being relatively straightforward to quantify.&lt;/p&gt;
&lt;p&gt;For instance, imagine a hypothetical service where sending a direct message (DM) requires six steps: “go to Home” &gt; “tap the DM icon” &gt; “write a message” &gt; “choose a recipient” &gt; “tap send” &gt; “confirm.” Judging whether these six steps are excessive or reasonable relative to the utility of the “send a DM” function is precisely the domain of usability.&lt;/p&gt;
&lt;p&gt;If usability is a question of “how many steps must be taken,” the next parameter, conveniency, is a question of “how natural those steps feel.” Conveniency is a subjective, qualitative attribute that refers to the degree to which a user can perform a function with ease. On the surface it’s easy to confuse with usability, but a closer look reveals a clear distinction.&lt;/p&gt;
&lt;p&gt;Let’s take a concrete example to understand the difference. When we look for a “save” function, we usually look first under a category like “File,” because “save” and “file” are conceptually related and are conventionally grouped together. This structure aligns with the user’s existing experience and cognitive expectations. If “save” instead belonged under “Tools” or “Edit,” users would struggle to find and perform the function. Even though the number of steps remains identical, the cognitive burden a user feels at each step changes entirely.&lt;/p&gt;
&lt;p&gt;Conveniency, then, is a metric closer to how well structure and placement align with user expectations — in other words, how easy something is to understand. Even with the same number of steps, high conveniency makes users feel that something is “easy,” while low conveniency makes them feel it is “complicated.”&lt;/p&gt;
&lt;p&gt;The last parameter, pleasurability, is the emotional satisfaction a user feels through the design — not far removed from the aesthetic value we typically associate with design. Visual beauty, the delight felt in an interaction, the emotional tone conveyed by a brand — all of this falls within this domain. Being positioned at the top of the pyramid among the three parameters, it is the most difficult attribute to work with, yet, interestingly, it is also the one most closely related to what designers have fundamentally done for centuries — the formative work of creating beauty and evoking emotion — which is why it is often achieved relatively easily.&lt;/p&gt;
&lt;p&gt;There is, however, one caveat worth noting here. Experience unfolds across time and space, and is formed through the integration of cognitive processes. No matter how beautiful a single screen may be, if it feels out of place within the overall flow of use or disconnected from what comes before and after, it can actually damage the user’s emotional experience. This is why, in this kind of emotional work, it is necessary to look closely not only at the completeness of individual elements but also at the process of their integration, and to carefully manage the user’s entire journey as a single, continuous emotional flow.&lt;/p&gt;
&lt;p&gt;Putting all of this together, UX design can be defined as the work of properly adjusting these three parameters to fit the intended experience. One thing worth noting here is that two of the three — conveniency and pleasurability — are subjective values.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Because users differ in the depth of their experience, cultural background, taste, and preferences, conveniency and pleasurability can be interpreted in completely different ways depending on how we define our users. What feels intuitive to one person may feel unfamiliar to another; what one person finds beautiful, another may find uncomfortable.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ultimately, “properly adjusting” these parameters is not a matter of finding a universal right answer, but a contextual judgment premised on a deep understanding of a specific group of users. This is precisely why, earlier in this series, when we declared what UX design is, we emphasized the deep dive into the user. Only by understanding users — not from the consumer’s perspective covered in market research, but from a more qualitative, in-depth perspective as the actual users of a product — can we design and establish a genuinely meaningful user experience.&lt;/p&gt;
&lt;h2 id=&quot;manipulating-the-parameters-to-fit-a-goal&quot;&gt;Manipulating the Parameters to Fit a Goal&lt;/h2&gt;
&lt;p&gt;There is a trap that’s easy to fall into at this point. Statements like “fewer steps means higher efficiency,” “making something intuitive makes it convenient,” and “clean and simple means beautiful” are all true in isolation. But if you try to maximize all three metrics simultaneously, or search for some ideal direction that satisfies every user, you risk losing sight of your goal and actually ruining the user experience. This is because trade-offs inevitably exist among the parameters — raising one frequently lowers another.&lt;/p&gt;
&lt;p&gt;Let’s take a concrete example. Some users may find the action of shaking a mobile device familiar and feel that it shortens the process, while others may not even think of shaking as a way to operate the device, and end up struggling far more than they would with an explicit, longer sequence of steps. A choice made to increase usability ends up decreasing conveniency instead.&lt;/p&gt;
&lt;p&gt;Similarly, some users may feel a sense of simplicity and aesthetic pleasure from an interface organized with icons alone, while others may feel frustrated because, without text labels, they cannot tell what each icon means. In this case, a choice made to increase pleasurability undermines conveniency instead. This is why finding the right balance among the three parameters matters, and the answer ultimately depends on which users and which experience we are aiming for.&lt;/p&gt;
&lt;p&gt;User experience is not something that can simply be calculated in numbers. Consider, for example, the recent shift in how cars handle gear shifting — from a physical lever to a dial or a set of buttons. Dial- or button-based controls save space and simplify the operating steps, but for drivers long accustomed to operating a lever, they can feel unfamiliar and unsettling instead. The same change can be an improvement in usability for one person, and a step backward in conveniency for another.&lt;/p&gt;
&lt;p&gt;Thinking about this a bit more deeply, every individual user carries their own prior experience, values, preferences, and level of digital literacy. These personal contexts combine to shape how a person perceives and evaluates a new product or service. This is why, looking at the very same interface, one person might feel it is “intuitive” while another feels it is “unfamiliar.”&lt;/p&gt;
&lt;p&gt;The role of UX design is precisely this: to pick out and make visible a target user group from among this diversity of users, to understand their experiences, values, and preferences in depth, and, on that foundation, to properly adjust and present the parameters of the user experience so as to realize the intended experience.&lt;/p&gt;
&lt;p&gt;In practical terms, this looks something like the following: within a complex web of intertwined functions, placing enough cognitive cues for users to feel that something is “intuitive” alongside the utility each individual function provides, blending in an overall sense of aesthetics, and offering a fitting reward — the function itself — in return for the effort the user invests. At the same time, making sure the process of use never jars or breaks at any single point, but instead flows as a consistent emotional journey from beginning to end that delivers the intended experiential value — these, most likely, are the standards by which good UX design should be judged.&lt;/p&gt;
&lt;p&gt;So far, we have looked at the three parameters of UX design and discussed how to adjust them. But it’s worth taking one more step here and asking a fundamental question: where, ultimately, do these three parameters — usability, conveniency, and pleasurability — get decided? Judging whether the number of steps is appropriate, whether the structure is intuitive, whether the aesthetics fit, is not something that happens on the pixels of a screen, but ultimately inside the mind of the user receiving it. No matter how precisely a designer adjusts these parameters, if they don’t understand how that adjustment is processed within the user’s cognition, the adjustment can only rely on baseless intuition.&lt;/p&gt;
&lt;p&gt;Adjusting these parameters means intervening in the user’s cognition, and this is why understanding the principles by which that cognition operates becomes an essential competency for a UX designer. In the next post, we will step into the structure of cognition itself and look at what actually happens inside the user’s mind.&lt;/p&gt;</content:encoded><category>The UX Design Manifesto</category><category>UX Design</category><category>Usability</category><category>Conveniency</category><category>Pleasurability</category></item><item><title>What Is MCP? — How AI Talks to Your Tools</title><link>https://blog.bepuljang.co.kr/en/vibecoding/06/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/vibecoding/06/</guid><description>Understand what MCP (Model Context Protocol) is, and how to connect external tools to Claude Code, step by step.</description><pubDate>Tue, 07 Jul 2026 15:45:16 GMT</pubDate><content:encoded>&lt;p&gt;So far, we’ve installed Claude Code, recorded context with CLAUDE.md, built our first screen, and looked at what to watch out for as a project grows. In this post, we introduce &lt;strong&gt;MCP (Model Context Protocol)&lt;/strong&gt;, which connects the AI to external tools so it can work far more powerfully.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-is-mcp&quot;&gt;What Is MCP&lt;/h2&gt;
&lt;p&gt;To understand the term MCP, let’s first look at the words Model and Protocol. The “model” here refers to an AI entity as the term is used in the AI industry — the same “model” as in “language model (LM).” And a “protocol” is an agreement, a set of rules, for communication between different systems. So MCP is a protocol for supplying context to a model.&lt;/p&gt;
&lt;p&gt;MCP was released in late 2024 by Anthropic, the maker of Claude. It’s now the industry-standard way for models to communicate, supported not just by Claude Code but by a wide range of AI tools. Once you connect MCP, the AI can directly pull information from external services (documentation sites, browsers, databases, collaboration tools, and more) or perform actions on them — so your tools and data work together as one integrated system.&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;&lt;/th&gt;&lt;th&gt;Without MCP&lt;/th&gt;&lt;th&gt;With MCP&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;What the AI can know&lt;/td&gt;&lt;td&gt;The conversation, open files&lt;/td&gt;&lt;td&gt;External service data too&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;How it works&lt;/td&gt;&lt;td&gt;Writes code only&lt;/td&gt;&lt;td&gt;Can look up and modify external services&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Example&lt;/td&gt;&lt;td&gt;”Fix this function&quot;&lt;/td&gt;&lt;td&gt;&quot;Find the latest docs for this library and apply them exactly”&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;In other words, MCP is like an expansion slot that gives the AI extra hands and eyes.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-kinds-of-mcp-can-you-connect&quot;&gt;What Kinds of MCP Can You Connect?&lt;/h2&gt;
&lt;p&gt;There are many kinds of MCP servers. Here are some go-to examples that are especially useful when you’re just getting started with vibe coding.&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;MCP Server&lt;/th&gt;&lt;th&gt;What It Does&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Context7&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Pulls up-to-date official docs for libraries/frameworks so code is written with the correct API&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Playwright&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Opens a real browser to click through and test the screens you’ve built&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Filesystem&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Reads and writes files in a folder you specify&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;GitHub&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Looks up issues/PRs and repository information&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Beyond these, there are MCPs for databases, design tools, project-structure tracking services, and more. We mentioned MCP earlier when introducing &lt;a href=&quot;https://app.umtri.io&quot;&gt;Umtri.io&lt;/a&gt; — connect it, and the AI can directly check the node structure, bugs, and history registered there and fold them into new plans, carrying the whole thing through as if it were a single service.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;connecting-an-mcp-to-claude-code&quot;&gt;Connecting an MCP to Claude Code&lt;/h2&gt;
&lt;p&gt;You connect them in the terminal with the &lt;code&gt;claude mcp add&lt;/code&gt; command. Each server connects a little differently (running locally, or reaching a remote URL), and the exact command is spelled out in the official docs or that service’s own instructions.&lt;/p&gt;
&lt;p&gt;For example, connecting a remote server (over HTTP) looks like this. The &lt;code&gt;YOUR_TOKEN&lt;/code&gt; part is a token issued by services that require a login.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;claude&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; mcp&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; add&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;nam&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  --transport&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; http&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  --url&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; https://example.com/mcp&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;  --header&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;Authorization: Bearer YOUR_TOKEN&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For a server that runs locally, it looks like this.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;claude&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; mcp&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; add&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;nam&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; npx&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -y&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &amp;#x3C;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;server-packag&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;e&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running the command registers that MCP server with Claude Code, and from then on the connection activates automatically every time you run Claude Code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Verifying the connection&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Start Claude Code and type &lt;code&gt;/mcp&lt;/code&gt; to see the list of currently connected MCP servers and their status.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;keep-your-auth-tokens-safe&quot;&gt;Keep Your Auth Tokens Safe&lt;/h2&gt;
&lt;p&gt;Some MCP servers require a login and ask for an API token. Since this token grants access to your account, putting it directly in a code file risks exposing it publicly on places like GitHub.&lt;/p&gt;
&lt;p&gt;The safe way to manage a token is with an environment variable.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Create a .env file&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;echo&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;SERVICE_TOKEN=your_token_here&quot;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; .env&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Add it to .gitignore so it doesn&apos;t get pushed to git&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;echo&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; &quot;.env&quot;&lt;/span&gt;&lt;span style=&quot;color:#F97583&quot;&gt; &gt;&gt;&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; .gitignore&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, in the MCP registration command, reference the environment variable instead of pasting the token value directly.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;when-is-a-good-time-to-add-an-mcp&quot;&gt;When Is a Good Time to Add an MCP?&lt;/h2&gt;
&lt;p&gt;You don’t need to connect several MCPs from the start. Basic development gets going just fine with Claude Code and CLAUDE.md alone. A good approach is to add “one MCP that removes a pain point” whenever a pain point actually shows up.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The AI keeps getting a library’s API wrong → connect &lt;strong&gt;Context7&lt;/strong&gt; for up-to-date docs&lt;/li&gt;
&lt;li&gt;Checking your screens by hand every time is tedious → use &lt;strong&gt;Playwright&lt;/strong&gt; for browser testing&lt;/li&gt;
&lt;li&gt;Your project has grown and you want to track structure/history automatically → look into a dedicated-service MCP&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the next post, we cover how to get exactly what you want from the AI — that is, prompt engineering.&lt;/p&gt;</content:encoded><category>Vibe Coding for Everyone</category><category>MCP</category><category>Claude Code</category><category>vibe coding</category></item><item><title>The Six Attributes of User Experience — The UX Pyramid</title><link>https://blog.bepuljang.co.kr/en/uxd-manifesto/04/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/uxd-manifesto/04/</guid><description>What does UX design actually work with? Using a framework called the UX Pyramid, we break experience down into six constituent attributes and look at where, among them, a designer can actually intervene.</description><pubDate>Tue, 07 Jul 2026 02:28:57 GMT</pubDate><content:encoded>&lt;p&gt;To design something is to adjust the various attributes that the object of design possesses, in line with a goal, and to propose the form that best fits that goal. Just as an architect calibrates a space’s dimensions, circulation, light, and materials, and a graphic designer works with color, typography, and the proportions of white space, the essence of every act of design ultimately lies in finding the optimal balance among the attributes an object holds.&lt;/p&gt;
&lt;p&gt;Designing a user’s experience is no different. The only distinction is that the object here is not a physical form or a visual expression, but the sequence of experiences a user goes through while using a product or service. Just as design in general has drawn out optimal outcomes by manipulating attributes such as aesthetics, functionality, and symbolism toward a given goal, the role of user experience design, too, is to identify the attributes inherent to experience and deliberately adjust them into a form optimized for the design goal. Naturally, this raises a question. What exactly are the attributes that make up an experience, how are they classified, and which of them can a designer actually adjust?&lt;/p&gt;
&lt;p&gt;To examine this, let’s first break experience down into its attributes using one of the more representative frameworks among the many theories on what constitutes user experience: the UX Pyramid. The UX Pyramid is a model that organizes the attributes of user experience into a hierarchy, structured so that it begins with the most basic and essential attributes and gradually ascends toward higher-order values. This framework lets us systematically separate out the various facets of what a user experiences, and gives us a clearer picture of what role each attribute plays within the experience as a whole.&lt;/p&gt;
&lt;h2 id=&quot;the-user-experience-pyramid-six-attributes-that-make-up-user-experience&quot;&gt;The User Experience Pyramid: Six Attributes That Make Up User Experience&lt;/h2&gt;
&lt;p&gt;According to the UX Pyramid model, user experience is made up of six layers stacked from the bottom up in this order: Functionality — Reliability — Usability — Conveniency — Pleasurability — Meaningfulness. The lower a layer sits, the more basic and essential the attribute; the higher it sits, the more it embodies a higher-order, qualitative value.&lt;/p&gt;
&lt;p&gt;The most intuitive way to understand how each layer of this pyramid actually operates is to recall a moment when a user felt “discomfort.” When a user experiences discomfort with a product or service, that discomfort may originate from one or more of the attributes above.&lt;/p&gt;
&lt;p&gt;It might be because the product’s or service’s functions are unsatisfying (Functionality), because it breaks down too often (Reliability), or because the steps required to use a desired function are too complicated (Usability). It might also be because it’s difficult to use (Conveniency), because it triggers an emotional aversion (Pleasurability), or because it doesn’t align with the user’s own values (Meaningfulness).&lt;/p&gt;
&lt;p&gt;Users typically lump their discomfort together as “I just don’t like it,” but beneath that phrase there is often a tangle of attributes from these different layers. Improving the user experience therefore requires first pinpointing exactly which attribute the discomfort stems from.&lt;/p&gt;
&lt;p&gt;Looking at these six attributes from a broader perspective, they fall into two distinct categories. One consists of attributes that are objective and can be indexed quantitatively — Functionality (Usefulness), Reliability, and Usability. These can be measured and compared using numbers and data.&lt;/p&gt;
&lt;p&gt;The other consists of attributes evaluated as subjective, qualitative experiential value — Conveniency, Pleasurability, and Meaningfulness. Even under identical conditions, these can feel entirely different from one user to another, and they belong to a domain better grasped through qualitative methods than through numerical measurement.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you’ve encountered Maslow’s hierarchy of needs, you may notice a similarity here. Like that pyramid of needs, this structure starts from the most basic, simple attributes at the bottom and gradually leads up to higher-order values that are difficult to quantify.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Each attribute can be briefly defined as follows.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Functionality: the functional utility that a product or service provides to the user.&lt;/li&gt;
&lt;li&gt;Reliability: the degree to which the functions a user expects are carried out without error.&lt;/li&gt;
&lt;li&gt;Usability: the number and complexity of steps a user must go through to carry out a given function.&lt;/li&gt;
&lt;li&gt;Conveniency: how easily a user can carry out a given function — its intuitiveness.&lt;/li&gt;
&lt;li&gt;Pleasurability: the emotional satisfaction a user feels.&lt;/li&gt;
&lt;li&gt;Meaningfulness: the sense of meaning a user feels fulfilled by.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once all six attributes are laid out, a natural question arises from a practical standpoint. Can a designer actually work with all of these attributes? The short answer is that the attributes user experience design can meaningfully engage with are the three in the middle — Usability, Conveniency, and Pleasurability.&lt;/p&gt;
&lt;p&gt;The reason becomes clear when you look at the top and bottom of the pyramid. At the bottom, Functionality and Reliability correspond to the technical capability that constitutes the product or service. A server going down or a core function failing to work cannot be solved through design alone — that belongs to the domain of engineering.&lt;/p&gt;
&lt;p&gt;At the very top, conversely, Meaningfulness concerns what meaning this product or service holds in the user’s life — a matter tied to the earliest stage of the product or service, namely the direction and vision of the business itself. Once that direction has already been set, it is difficult to bring about meaningful change through design.&lt;/p&gt;
&lt;p&gt;In the end, the domain where a UX designer can most directly exercise their expertise is the three attributes that sit in between. From this perspective, I personally refer to these three attributes — the ones through which UX design chiefly does its work — as the “UX Design Parameters.”&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The term “UX Design Parameters” is my own personal coinage, not a formalized theoretical term, so it should be used with that caveat in mind.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now that we’ve identified where, among the six attributes, a designer’s hand can actually reach, the next post will look closely at each of these three parameters — Usability, Conveniency, and Pleasurability — one at a time.&lt;/p&gt;</content:encoded><category>The UX Design Manifesto</category><category>UX Design</category><category>UX Pyramid</category><category>User Experience</category><category>Design Theory</category></item><item><title>The Vibe Coding Trap — When Easy Makes Things Harder</title><link>https://blog.bepuljang.co.kr/en/vibecoding/05/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/vibecoding/05/</guid><description>As you grow a project with vibe coding, a mounting misunderstanding of your own project builds up. This is a story about the frustration that brings, and how to work through it.</description><pubDate>Tue, 07 Jul 2026 02:28:57 GMT</pubDate><content:encoded>&lt;p&gt;The first few days are genuinely amazing. You describe an idea, code appears, the screen changes. Every time a feature gets added, you feel a real sense of accomplishment. But once the project grows past a certain point, this process gradually starts to get harder.&lt;/p&gt;
&lt;p&gt;For example, you say “add a login feature,” and code you’d spent a while building conflicts with the existing code, breaking the project itself. The AI service spends all day analyzing where the conflict is — but despite its “found it” verdict, every fix keeps throwing errors, tokens and time drain away, and the problem stubbornly refuses to resolve.&lt;/p&gt;
&lt;p&gt;This is the trap of vibe coding — and of AI services that seem to know everything, and that phrase their messages to make you believe it too.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;There are two reasons your project structure falls apart as you keep adding features with vibe coding.&lt;/p&gt;
&lt;h3 id=&quot;the-ai-sees-less-than-youd-think&quot;&gt;The AI sees less than you’d think&lt;/h3&gt;
&lt;p&gt;An AI service writes code by referring to the files currently open, the conversation in the current session, and maybe your CLAUDE.md. You remember what you built or changed earlier, and you assume the AI — the very one that made it — remembers too. But in reality it only grasps what it strictly needs; it doesn’t perfectly take in everything each time it writes new code.&lt;/p&gt;
&lt;p&gt;If the AI service did parse the entire codebase and every spec on every message and edit, it would be far less efficient than it is now — so this is actually a sensible design. The problem is that, because of it, when you order a design that conflicts with older code in particular, the AI doesn’t realize it and works focused solely on doing the current request well. Behind that upbeat, confident tone, it’s working without knowing very much.&lt;/p&gt;
&lt;h3 id=&quot;you-dont-know-the-structure-either&quot;&gt;You don’t know the structure either&lt;/h3&gt;
&lt;p&gt;Vibe coding being easy is only true in the realm of “ordering.” If anything, the code itself has been written countless times in a style you don’t know, without you really looking at it — so you often have no idea how it’s put together or what risks it was built with.&lt;/p&gt;
&lt;p&gt;So when a problem arises, you end up depending on the AI’s code analysis after all. And when that analysis is a misjudgment, you circle around the problem while it sits right beside you — code fixed on a wrong diagnosis breeds new problems, and you fall into an endlessly repeating loop of errors.&lt;/p&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Stage&lt;/th&gt;&lt;th&gt;Symptom&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Project start&lt;/td&gt;&lt;td&gt;Features get built anew; things come together smoothly, just as you say them.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Project growth&lt;/td&gt;&lt;td&gt;The project grows and gets complex, gradually becoming a black box whose state you no longer know.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Problem appears&lt;/td&gt;&lt;td&gt;Adding a new feature breaks an existing one. The AI analyzes the cause — but misjudges it.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Problem grows&lt;/td&gt;&lt;td&gt;Fixes made on that misjudgment create new bugs.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Infinite loop&lt;/td&gt;&lt;td&gt;Bugs pile on bugs until analysis itself becomes hard; rebuilding from scratch would be better.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&quot;how-not-to-fall-into-the-error-trap&quot;&gt;How Not to Fall into the Error Trap&lt;/h2&gt;
&lt;p&gt;The direct solution to this problem may well be coding skill and experience. But since falling into this trap is itself the problem, preventing it is what matters — and for that, what you need is &lt;strong&gt;understanding your project’s structure and passing along, in your prompts, the things the AI service needs to watch out for&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If you know what’s been built, what’s connected to what, and when each feature was added — and how a new structure will slot in and what risks lurk there — you can give the AI the context it needs: “When you add this new feature, take into account these points in our current project structure.” That way you can see the traps in advance and avoid them.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;several-ways-to-understand-and-record-structure&quot;&gt;Several Ways to Understand and Record Structure&lt;/h2&gt;
&lt;p&gt;The approaches below are widely used for understanding and recording structure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Documentation files inside your project&lt;/strong&gt; — Markdown files like &lt;code&gt;CLAUDE.md&lt;/code&gt; or &lt;code&gt;ARCHITECTURE.md&lt;/code&gt;, covered back in part 3, record your folder structure and conventions and help head off conflicts that can arise during edits. You can start right away without any extra tools, and Claude Code reads them automatically at the start of a conversation. The downsides: because they’re just documents, there’s no tool to verify them — if they drift from reality they can actually cause problems — and there’s a limit to how well a person can read and absorb them.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note-taking tools&lt;/strong&gt; — Keeping a log of feature additions and structural notes in something like Notion or Obsidian. You can write freely without worrying about format, but since the record lives apart from your code, it can go “stale.” You have to manage it meticulously and diligently, and pass along exactly what’s needed in your prompts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Diagramming tools&lt;/strong&gt; — Drawing your structure with Mermaid or Excalidraw. This is the easiest form for a person to understand, and great for grasping the whole picture at a glance — but every structural change means reworking the drawing, and it ends up duplicating what’s in your CLAUDE.md, which makes it less efficient.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href=&quot;https://app.umtri.io&quot;&gt;Umtri.io&lt;/a&gt;&lt;/strong&gt; — Umtri.io is a project-tracking tool BEPULJANG built to solve exactly these problems. It connects directly to AI services via MCP (Model Context Protocol), recording the project the AI builds and showing it visually through a UI; when you plan new work, it analyzes the nodes and relationships being added and automatically flags points where errors could arise. Once your project has grown beyond a certain point, adopting it can reduce risk and deepen your understanding of the project.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;when-and-how-to-start-recording&quot;&gt;When and How to Start Recording&lt;/h2&gt;
&lt;p&gt;You don’t need to define the perfect structure right from the start. A good time to begin is &lt;strong&gt;once your first core feature is complete&lt;/strong&gt;. At that point, write down your current folder structure and the role of your main files in &lt;code&gt;CLAUDE.md&lt;/code&gt; once, and then just append whatever changes each time you add a feature.&lt;/p&gt;
&lt;p&gt;The key is consistency, not volume. One line when you make a new component, one line when your data structure changes. As these small records pile up, a few weeks later you can hand the AI context by saying, “Here’s what our project structure looks like now.”&lt;/p&gt;
&lt;p&gt;In the next post, we’ll cover how to connect external tools to Claude Code — that is, MCP. It came up briefly while introducing Umtri.io; in the next post, we’ll take a closer look at what MCP actually is.&lt;/p&gt;</content:encoded><category>Vibe Coding for Everyone</category><category>Vibe Coding</category><category>Project Structure</category><category>Documentation</category></item><item><title>What Is Experience? — User Experience as a Process of Acquisition</title><link>https://blog.bepuljang.co.kr/en/uxd-manifesto/03/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/uxd-manifesto/03/</guid><description>Experience is a process of acquisition. Starting from everyday language and dictionary definitions, we examine how experience is formed through cognition and behavior, sequence and irreversibility — and reconsider what a UX designer actually does within that process.</description><pubDate>Mon, 06 Jul 2026 01:22:05 GMT</pubDate><content:encoded>&lt;p&gt;In the previous piece, we made clear our direction: to empathize from the user’s perspective rather than the consumer’s, and to step directly into their environment of use in order to improve their experience. If the declaration has set the direction, it is now time to actually move along it. To do that, we must first understand what “experience” is, and grasp how it can be deliberately shaped.&lt;/p&gt;
&lt;p&gt;Borrowing again the diving metaphor for UX design from the previous piece: the water into which the diver plunges — that is, experience — corresponds to a river flowing ceaselessly and inexorably onward. The river flows at its own pace and in its own direction regardless of the diver’s will, and the diver who enters it feels the river’s nature with their entire body. And what determines the character of that river is precisely its headwaters. Where the source begins and what terrain it passes through shape the water quality and direction of the river we call experience.&lt;/p&gt;
&lt;h2 id=&quot;experience-something-we-already-know&quot;&gt;Experience: Something We Already Know&lt;/h2&gt;
&lt;p&gt;So what, concretely, is experience? To understand this, let’s first look at how we exchange experience in everyday life. When we ask about or answer questions of experience, we typically ask “how was it” or “what do you think” about some object. Questions like “How was that restaurant?” or “Is your new phone any good?” are exactly this kind. Both share the trait of being questions about the past. Since we cannot ask about experience with something not yet undergone, we can take experience, for now, to be some kind of memory of interactions — things that happened in the past with respect to an object.&lt;/p&gt;
&lt;p&gt;And if we recall the answers we actually give, most of them turn out to be emotional evaluations — “good,” “bad,” “comfortable,” “uncomfortable,” “nice,” “not great,” and the like. Of course, for parts that left a particularly strong impression, we might add an analytical remark such as “this specific part was uncomfortable in this specific way” or “this particular point was especially good.” But in general, a handful of dominant emotions form the core of the answer, and whatever is added afterward mostly functions to support or justify that emotion. This is also why, when we recall an experience, an overall feeling comes to mind before any fine-grained facts do. Putting these observations together, then, we can define experience as “an integrated emotional memory of interactions that occurred with an object in the past.”&lt;/p&gt;
&lt;p&gt;The definition above was derived inductively from our everyday observations. Interestingly, though, if we set this empirically drawn definition aside and look at the dictionary definition instead, experience turns out to be summarized in a strikingly similar way. Here is how a dictionary defines it:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;ex·pe·ri·ence&lt;/strong&gt; /ikˈspirēəns/&lt;/p&gt;
&lt;p&gt;the process of getting knowledge or skill from doing, seeing, or feeling things&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Recast in our own terms, this dictionary definition tells us that experience is the process of acquiring knowledge or skill from external stimuli. The essence of UX design is compressed into this short definition, and the two keywords that deserve special attention here are “acquisition” and “process.” What matters is that experience is not a simple outcome but is formed through the act of “acquisition,” and that it unfolds in time as a “process.” In other words, this “process of acquisition” is precisely the fundamental principle by which experience is formed.&lt;/p&gt;
&lt;p&gt;Understanding this principle brings the essence of what UX design does into sharper focus. Doing UX design means intervening in this process of acquisition and delicately manipulating it, so as to change the water quality and flow of the river we call experience in the direction we want. Let’s now look more closely at what “acquisition” and “process” each specifically mean, and how they connect to design practice.&lt;/p&gt;
&lt;h2 id=&quot;the-process-of-acquisition&quot;&gt;The Process of Acquisition&lt;/h2&gt;
&lt;p&gt;Let’s start with “acquisition.” Put another way, acquisition is the input of information — that is, Cognition. Through our sensory organs — eyes, ears, nose, skin — we take in information about an object’s various facets: its shape, color, texture, sound, smell. Processing that information in the brain and storing it as memory is the sequence of events that constitutes “acquisition.”&lt;/p&gt;
&lt;p&gt;But here a fundamental difficulty emerges — one that every UX designer must confront. Because this process of cognition is largely an internal phenomenon taking place inside the user’s head, current technology makes it practically impossible to observe or precisely analyze it directly. There is no way to look into what a user feels or what judgment they make, in real time, as they look at a product. This forces the designer who practices UX design to infer the invisible internal state of cognition from the user’s visible, surface-level responses. The unconscious representation that a user shows, naturally and without awareness, in response to an object is what we call “Behavior.” Behavior is like the shadow of cognition — it is nearly the only window through which we can indirectly read cognition that cannot be seen directly.&lt;/p&gt;
&lt;p&gt;If acquisition corresponds to the “what” of experience, “process” corresponds to its “how.” Looking at this “process,” even when an experience lasts no more than a fleeting instant in time, some flow inevitably exists within it. Experience is formed through an analyzable Sequence — first encountering a product, then using it, then storing or carrying it, and so on. Even the experience of drinking a cup of coffee, for instance, passes through a sequence: the moment of picking up the cup, the temperature meeting the lips, the taste of the first sip, the weight felt when setting it back down.&lt;/p&gt;
&lt;p&gt;And because time inevitably entails space, experience — however small in scale — is always formed, unit by unit, through a definite temporal and spatial procedure. This procedural nature is closely tied to the improvement of experience, since the quality of the overall experience shifts depending on where in the sequence, and what kind of change, is introduced. Also not to be overlooked here is the irreversibility that time and space possess — their inability to be undone. A moment once passed never returns, and the impression a user forms at a particular point in time colors the entirety of the experience that follows. This irreversibility makes unmistakably clear how important it is to manage every single moment in the procedure through which experience accumulates.&lt;/p&gt;
&lt;h2 id=&quot;between-cognition-and-behavior--the-role-of-the-ux-designer&quot;&gt;Between Cognition and Behavior — The Role of the UX Designer&lt;/h2&gt;
&lt;p&gt;Let’s now bring the two axes of acquisition and process together for an interim synthesis. Designing an experience, in the end, can be summarized as a series of tasks: observing the user’s cognitive process through the window of behavior, designing the object based on the insight gained from that observation so as to induce a change in behavior in the desired direction, and ultimately pursuing a change in cognition. In other words, it is an indirect yet intricate undertaking — observing visible behavior in order to change invisible cognition, and then changing the visible object again in order to affect that invisible cognition. In this process, what matters above all is to continually cross the boundary of the user’s unconscious — the boundary earlier likened to the “surface of the water” — checking whether the user’s behavior is being steered in the desired direction, and to synchronize and empathize with the user in order to infer their experience. In the end, we might say that the UX designer is someone who converses with the user’s unconscious.&lt;/p&gt;
&lt;p&gt;The fact that experience design deals with the invisible domains of cognition and behavior naturally causes the competencies it demands to differ considerably from those of existing consumer-oriented design. And this difference in required competency is what led UX design to be newly pioneered as an independent discipline and field in its own right. In existing design, the core competency lay in presenting styles and qualities that would appeal to the consumer, through the designer’s personal inspiration and intuition — the formative ability to create beautiful shapes, a sense for reading trends, and expressive skill in delivering visual impact were what mattered most.&lt;/p&gt;
&lt;p&gt;In UX design, by contrast, what matters is how deeply one can empathize with and understand the user’s perspective, in order to accurately infer their inner state even where it is not outwardly visible. This is territory that the mere “ability to make things pretty” cannot reach; it requires a broad understanding of human cognition and behavior. IDEO’s 1999 “Reimagining the Shopping Cart” project illustrates this well. Looking at the backgrounds of the people who took part, we find an astonishingly diverse group — industrial designers and engineers, but also people from psychology, architecture, business, linguistics, and biology — coming together as a single team to design one single product: the shopping cart. Why would designing a single product require experts from so many different fields? Because truly understanding the user requires more than the viewpoint of any one field; mobilizing diverse perspectives and competencies capable of illuminating the complex phenomenon of human experience from multiple angles is essential.&lt;/p&gt;
&lt;p&gt;Let’s draw all of this discussion together. Good UX design can be summarized as design that closely observes the user’s cognitive process, discovers the qualitative or behavioral problems that arise within it, and, through design, resolves them naturally — without the user even noticing. Rather than making the user consciously think “something has changed,” making them feel that “somehow this is more comfortable now” or “this just works naturally” is what good UX design aims for.&lt;/p&gt;
&lt;p&gt;To close by bringing back the river metaphor borrowed at the start of this piece: just as finding a river’s headwaters, improving its water quality and volume, and carefully guiding its course produces a river that is clean, stable, and beautiful, the role of the UX designer is to find the headwaters of experience and change its flow. Of course, compared to the immense flow of experience a user has accumulated, the individual adjustments a designer makes may at first seem far too minor. One might well doubt how changing one or two interactions could possibly change the entire experience.&lt;/p&gt;
&lt;p&gt;But in the end, every great flow is made by the accumulation of small things. Just as a river is ultimately formed by the confluence of countless small streams, changing the details of an experience one by one will, at some point, bring about a turning point where the overall flow suddenly draws much closer to the design goal. Enduring this process, and pressing steadily forward by trusting the weapons of observation and empathy rather than relying on one’s own intuition or inspiration — this is at once the greatest competency demanded of a UX designer and the hardest homework they must do.&lt;/p&gt;
&lt;p&gt;Now that we’ve confirmed experience is a process of acquisition, the next post will look at the concrete attributes that make it up — the six parameters that constitute experience — and identify which of these parameters UX design primarily deals with.&lt;/p&gt;</content:encoded><category>The UX Design Manifesto</category><category>UX Design</category><category>Experience</category><category>Cognition</category><category>Behavior</category></item><item><title>Building Your First Screen with Vibe Coding — Turning Ideas into Code</title><link>https://blog.bepuljang.co.kr/en/vibecoding/04/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/vibecoding/04/</guid><description>How to turn an idea into a prompt, and how the back-and-forth with AI takes shape into finished code — explained through a hands-on example.</description><pubDate>Mon, 06 Jul 2026 01:22:05 GMT</pubDate><content:encoded>&lt;p&gt;Now that you’ve written your CLAUDE.md, you’re ready to start actually building. In this post, we’ll walk through the entire process of using Claude Code to build the first screen of a simple web service, from start to finish.&lt;/p&gt;
&lt;p&gt;As our example, we’ll build a &lt;strong&gt;bookmark app&lt;/strong&gt; (a service for collecting links) — “a simple service for saving links to read later.” It’s not complicated, but it has a structure similar to a real service, which makes it a great way to get a feel for vibe coding.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;how-to-turn-an-idea-into-a-prompt&quot;&gt;How to Turn an Idea into a Prompt&lt;/h2&gt;
&lt;p&gt;The first skill you need to learn in vibe coding is &lt;strong&gt;describing your idea in as much detail as possible&lt;/strong&gt;. People with illusions about AI services think that, unlike a person, an AI will hand you what you want even from a vague request. But that only holds at the level of something built for fun — in an industrial project, where a result that deviates from expectations counts as a defect, it doesn’t. For a person and an AI alike, the range of things you could imagine from a vague request is simply too wide, so you have to spell things out to narrow that gap and get the result you expected.&lt;/p&gt;
&lt;p&gt;In other words, the difference between a good prompt and a bad one comes down to specificity.&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Type&lt;/th&gt;&lt;th&gt;Example Prompt&lt;/th&gt;&lt;th&gt;Problem&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Bad prompt&lt;/td&gt;&lt;td&gt;”Build me a bookmark app”&lt;/td&gt;&lt;td&gt;No idea what technology to use or what it should look like&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Average prompt&lt;/td&gt;&lt;td&gt;”Build me an app for saving bookmarks with React”&lt;/td&gt;&lt;td&gt;Specifies the technology, but features, design, and scope are unclear&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Good prompt&lt;/td&gt;&lt;td&gt;See below&lt;/td&gt;&lt;td&gt;Covers technology, features, visual goals, and constraints&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Here’s how you’d write a good first prompt:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Build me a simple web app that saves bookmarks and shows them as a list. When I enter a URL and title, it should be added to the list. Show the saved list as cards, and each card should have a delete button so I can remove items too. Let&apos;s build the bare minimum first and add the details as we go. I&apos;m thinking of building it with React and Vite — how would you approach it?&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Principles for writing prompts&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Be clear about what to build&lt;/strong&gt;: Write down, in as much detail as you can, the core features the project must have and the shape they should take.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Limit the scope of features&lt;/strong&gt;: Don’t ask for too much at once. Start small and add things step by step.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ask about what you don’t know&lt;/strong&gt;: You don’t have to force every spec into place. Rather than handing over everything at once, decide together — asking about what you’re unsure of, just like a real conversation with a person.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&quot;the-back-and-forth-flow-of-finishing-code-with-ai&quot;&gt;The Back-and-Forth Flow of Finishing Code with AI&lt;/h2&gt;
&lt;p&gt;When you enter the prompt above into Claude Code, the AI creates the project structure and writes the files. From here, &lt;strong&gt;the process of refining the result through conversation&lt;/strong&gt; begins.&lt;/p&gt;
&lt;h3 id=&quot;step-1-create-the-project&quot;&gt;Step 1: Create the Project&lt;/h3&gt;
&lt;p&gt;Move to your working folder in the terminal, then run Claude Code.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Create a working folder&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;mkdir&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; bookmark-app&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;cd&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; bookmark-app&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Run Claude Code&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;claude&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once Claude Code starts, you’ll see a &lt;code&gt;&gt;&lt;/code&gt; prompt. Enter the prompt you prepared above.&lt;/p&gt;
&lt;p&gt;As the AI starts working, you’ll see messages like these:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Creating project structure...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Writing src/App.jsx...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Writing src/components/BookmarkList.jsx...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Writing src/components/AddForm.jsx...&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can watch in real time as files are created and code is written. It’s especially worth watching closely at the beginning: the less of a codebase there is, the more often the AI heads in the wrong direction or misreads the project’s intent. If it isn’t writing what you meant, stop it at any point and steer it back toward your intent with a correcting prompt.&lt;/p&gt;
&lt;h3 id=&quot;step-2-try-running-it&quot;&gt;Step 2: Try Running It&lt;/h3&gt;
&lt;p&gt;Once the files are created, Claude Code will tell you how to run the project. For a React + Vite project, for example, it’s usually like this:&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;npm&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; install&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;npm&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; dev&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Type the address shown in the terminal (something like &lt;code&gt;http://localhost:5173&lt;/code&gt;) into your browser, and you’ll see the screen you just built.&lt;/p&gt;
&lt;h3 id=&quot;step-3-request-changes&quot;&gt;Step 3: Request Changes&lt;/h3&gt;
&lt;p&gt;Looking at the result, there will probably be parts you don’t like. When that happens, ask Claude Code for specific changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example design change request&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Add a light gray background (#f5f5f5) to the cards, and widen the spacing between them a bit.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Change the delete button to the icon I added in the assets folder.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Example feature change request&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;When adding a bookmark, if the URL doesn&apos;t start with http:// or https://,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;show a warning that says &quot;Please enter a valid URL.&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s important to request changes in small increments like this. If you ask for too many changes at once, the AI can end up changing the code in unexpected ways.&lt;/p&gt;
&lt;h3 id=&quot;step-4-repeat&quot;&gt;Step 4: Repeat&lt;/h3&gt;
&lt;p&gt;Repeat the cycle of modify → check → modify again. This repetition matters: every act of creation is built by stacking up thin but well-finished pieces, and vibe coding is no different.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;checking-the-result&quot;&gt;Checking the Result&lt;/h2&gt;
&lt;p&gt;It depends on the project, but for the React + Vite setup used in this example, keeping the dev server open reflects your changes in real time. Check the result in whatever way suits your project’s foundation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;e.g., Vite dev server running (Terminal)&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  VITE v5.x.x  ready in 300 ms&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ➜  Local:   http://localhost:5173/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ➜  Network: http://192.168.x.x:5173/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In that case, just enter &lt;code&gt;http://localhost:5173/&lt;/code&gt; in your browser.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Frequently used dev server commands&lt;/strong&gt;&lt;/p&gt;

























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Command&lt;/th&gt;&lt;th&gt;Purpose&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;npm run dev&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Start the dev server (with auto-refresh)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Ctrl + C&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Stop the dev server&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;npm run build&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Generate production files (used later)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;npm run preview&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Preview the built files locally&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&quot;common-problems-and-how-to-fix-them&quot;&gt;Common Problems and How to Fix Them&lt;/h2&gt;
&lt;p&gt;When building your first screen with vibe coding, you’ll often run into problems. AI services frequently recognize and resolve issues on their own — but when the AI can’t read the error message directly, or when the problem only reproduces in a separate environment, you’ll need to describe and help analyze the situation yourself.&lt;/p&gt;
&lt;p&gt;Usually, when an error occurs, a message is printed in red text in the terminal. Just copy it as-is and paste it into Claude Code.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;I&apos;m getting the error below. Please fix it.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;[Paste the full error message here]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even in coding done entirely by hand, this troubleshooting process is the hardest and most important part — it’s no exaggeration to say that doing it well decides whether vibe coding succeeds or fails. This is where a great deal of experience and skill come into play.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-weve-built-so-far&quot;&gt;What We’ve Built So Far&lt;/h2&gt;
&lt;p&gt;By the end of this stage, you’ll have a working first version of the bookmark app running in your browser. It only works locally (on your own computer) for now, and the saved data only survives a refresh — but &lt;strong&gt;your idea has become a screen that actually works.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In the next post, we’ll cover a problem you’ll often run into during vibe coding: how project structure tends to fall apart the more features you add, and how to manage it.&lt;/p&gt;</content:encoded><category>Vibe Coding for Everyone</category><category>Vibe Coding</category><category>Claude Code</category><category>Prompt</category><category>React</category></item><item><title>Why UX Design Emerged — From Consumer to User</title><link>https://blog.bepuljang.co.kr/en/uxd-manifesto/02/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/uxd-manifesto/02/</guid><description>UX design did not appear overnight. Tracing how design&apos;s focus shifted from ornamentation to product planning and then to user cognition, we examine the background behind the declaration that is &apos;UX design.&apos;</description><pubDate>Sun, 05 Jul 2026 12:12:20 GMT</pubDate><content:encoded>&lt;p&gt;To understand how UX design differs from design as it existed before, we first need to look at the currents through which the field of design itself has changed over time. Revisiting the history of design is not mere reminiscence — it is a necessary step toward understanding why UX design had to emerge in the first place.&lt;/p&gt;
&lt;p&gt;UX design did not fall from the sky one day. The growth of industry, the advance of technology, and shifts in the structure of society handed design a set of challenges qualitatively different from anything it had faced before, and UX design took shape naturally as a response to those challenges. Without understanding this flow, it is difficult to fully grasp the essence of UX design.&lt;/p&gt;
&lt;h2 id=&quot;abundance-of-goods-from-ornamentation-to-product-appeal&quot;&gt;Abundance of Goods: From Ornamentation to Product Appeal&lt;/h2&gt;
&lt;p&gt;Let’s start from the beginning. The modern concept of industrial design is generally understood to have originated in craft. In the early stages of industry, goods were scarce, and what people needed most urgently was immediate functionality — usefulness. Whether a product properly performed its intrinsic function, whether it faithfully served its role as a tool, mattered more than anything else at the time.&lt;/p&gt;
&lt;p&gt;Because of this, the functionality secured through the craftsman’s work was prioritized above any higher-order experience, and design as an activity was not yet recognized as independent — it remained a secondary domain attached to the realization of function. Against this backdrop, the earliest form of design served as ornamentation, delivering aesthetic value to a segment of the upper class who wanted a use experience beyond basic function. In simple terms, it was a division of labor in which the craftsman handled function and design layered beauty on top of it. This early division of roles left a surprisingly deep-rooted impression — it is precisely why, even today, hearing the word “design” tends to call to mind aesthetics or high added value before anything else.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Higher-order experience” is a concept from the UX pyramid perspective, which we cover in more detail later in this series.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;But this arrangement did not last. As industry matured and goods became abundant, satisfying functionality alone stopped being a point of differentiation. Once most products performed their basic functions well enough, consumers naturally began wanting something beyond mere functionality, and the consumer market expanded rapidly on that demand.&lt;/p&gt;
&lt;p&gt;Aesthetics and emotional appeal began to occupy important metrics from a marketing standpoint, and meeting that demand clearly could not be achieved by the old approach of “adding ornamentation after the fact to a form built for function.” The form of the product itself had to embody aesthetic and emotional value from the very start, and that required design to take the lead at the front end of the process rather than trailing at the back end. Accordingly, the order of the industrial process itself began to reverse. Design started presenting form and blueprint ahead of the craftsman, and the craftsman’s role shifted to implementing function according to that plan.&lt;/p&gt;
&lt;p&gt;This was the turning point at which design stopped being an accessory to function and took on a leading role in product planning. And in this process, design’s attention naturally shifted toward what would sell well on the shelf — that is, toward the “consumer.” Observing what draws consumers in, which trends and lifestyles dominate the market, and leading those trends became design’s core task.&lt;/p&gt;
&lt;h2 id=&quot;the-advance-of-technology-interfaces-grow-complex&quot;&gt;The Advance of Technology: Interfaces Grow Complex&lt;/h2&gt;
&lt;p&gt;Design was settling into consumer-centered planning, but this alone was not enough. Design, which up to that point had remained a one-off exercise at the moment of marketing — an “appeal in front of the shelf” — was called on to undergo one more fundamental shift, and what triggered that shift was social change, and above all the rapid advance of technology.&lt;/p&gt;
&lt;p&gt;The development of semiconductors and integration technology, for example, led to a movement of product convergence. Products that had previously existed as separate, individually specialized devices began merging into a single unit. In a world where the camera, the music player, the telephone, and the notepad had each existed on their own, these began converging into one device.&lt;/p&gt;
&lt;p&gt;In the earlier environment of divergence, the physical form of an individual product was closely tied to its function, and that connection was generally intuitive. A camera looked like a camera and a phone looked like a phone, so one could infer roughly how to use something just by looking at its shape. But in an environment where multiple devices merged into one, the same interface now had to perform several different functions at once. As a result, interfaces inevitably became layered, and conveying this layered functional structure to users required a far greater volume of cognitive cues and guidance than before.&lt;/p&gt;
&lt;p&gt;On top of the convergence problem, the advance of wireless technologies — RF, Bluetooth, Wi-Fi — and the spread of soft-key interfaces also played an important role in this shift. Previously, the user’s environment had been constrained by physical wires or fixed buttons, and that constraint, paradoxically, clearly defined how a device was meant to be used. But as wireless technology and soft keys freed users from these physical limitations, the user’s environment became far more open than before.&lt;/p&gt;
&lt;p&gt;The trouble was that this freedom was a double-edged sword. From the provider’s side, it became much harder to guide and predict user behavior, and eliciting a desired action now demanded a wholly new level of effort. The same was true from the user’s side. The formal cues indicating which part to interact with, and how, to access a desired function had grown ambiguous, so users badly needed usage guidance they could sense and follow intuitively.&lt;/p&gt;
&lt;p&gt;In short, the industry as a whole began to require closer observation of and greater care for the user’s cognitive process and procedure of use. And design — the field traditionally responsible for sensory perception and form — took this challenge on directly, and for the first time began looking at the “user” rather than the “consumer.” The spread of the smartphone stood as the emblem and symbol of this entire shift, driving transformative change.&lt;/p&gt;
&lt;h2 id=&quot;a-declaration-to-look-at-the-user-not-the-consumer&quot;&gt;A Declaration to Look at the “User,” Not the Consumer&lt;/h2&gt;
&lt;p&gt;UX design emerged out of this very upheaval. As mentioned earlier, design had always, even before this, given thought to the user’s experience in some form. And yet, coining the term “user experience design (UX design)” to explicitly separate this concept from existing design is itself a kind of declaration — a fundamental shift in perspective.&lt;/p&gt;
&lt;p&gt;That is because the user’s experience that earlier design concerned itself with was largely confined to the user as a “consumer,” seen from a marketing standpoint. In other words, the focus was on the appeal felt at the moment of purchase. The core task was to present features that looked better than competing products on the shelf, and planning was largely driven by sensory appeal — what material the kettle’s handle should be made of, what sound it should make when the water boils, and so on. This is, of course, an important concern, but it is a different matter altogether from looking deeply into the ongoing relationship a user forms with a product after it has been purchased.&lt;/p&gt;
&lt;p&gt;User experience design, by contrast, starts from a different depth altogether. It sets out to look at the procedure itself — how the user actually uses the product after it has been purchased. It declares an intent to synchronize with the user within the flow of use, to examine whether any procedure poses a problem from the standpoint of the user’s cognitive experience, and, if so, what solution is needed to resolve it. This amounts to moving past the surface-level appeal that the consumer perspective dealt with and diving deep into the water of the user’s underlying cognition. It is a shift that pulls even the cognitive processes taking place inside the user’s head into the domain of design, beyond the visible form and feel.&lt;/p&gt;
&lt;p&gt;Let’s define the depth of this dive a bit more precisely. The core of user experience design lies in synchronizing with the environment of use, and in understanding — and deliberately manipulating — the cognitive procedures and events that occur within that environment. Grasping what the user sees, what they expect, and in what order they act, and designing the experience to fit that flow, is the essence of UX design.&lt;/p&gt;
&lt;p&gt;If planning is carried out at this level, it can be called “UX design–oriented” regardless of what form it ultimately takes. Conversely, no matter how faithfully the methodology of UX design is followed, if it fails to genuinely empathize with the user and analyze their cognitive process, it has merely borrowed the tools and can hardly be said to have been carried out as UX design. It is perspective, not form, that defines UX design.&lt;/p&gt;
&lt;h2 id=&quot;not-progress-but-a-new-perspective&quot;&gt;Not Progress, But a New Perspective&lt;/h2&gt;
&lt;p&gt;One balanced point is worth adding here. Just as the new is not always right and the old is not always wrong, it would also be a mistake to assume that UX design is unconditionally superior to consumer-oriented design. Design must always follow the goals and efficiency of the given project, and UX design should simply be adopted whenever the context calls for it.&lt;/p&gt;
&lt;p&gt;For a project that genuinely calls for deep consideration of the user’s experience and cognitive flow, UX design should certainly be carried out — but forcing a logical framework onto a situation where the answer is already clear is, if anything, dangerous. To return to the diving metaphor used earlier: diving into shallow water gets you hurt. Pushing a UX design process through on a project that lacks the cognitive depth worth diving into produces results that fall well short of the cost and time invested. The value of UX design comes from the appropriateness of the context in which it is applied.&lt;/p&gt;
&lt;p&gt;We have now looked at why UX design emerged and how it takes a different perspective from existing design. From here, the natural next question follows: what, exactly, is this “experience” — the object of the dive that UX design seeks to engage with? By what principle does it take shape, and how might we steer it in the direction we want?&lt;/p&gt;
&lt;p&gt;The declaration of UX design is now complete. The next piece turns to the very thing that declaration is aimed at — what “experience” actually is.&lt;/p&gt;</content:encoded><category>The UX Design Manifesto</category><category>UX Design</category><category>Design History</category><category>User Experience</category><category>Interface</category></item><item><title>CLAUDE.md — How AI Understands and Remembers Your Project</title><link>https://blog.bepuljang.co.kr/en/vibecoding/03/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/vibecoding/03/</guid><description>Learn how to write a CLAUDE.md file that helps Claude Code remember your project&apos;s structure and rules, with good and bad examples.</description><pubDate>Sun, 05 Jul 2026 12:12:20 GMT</pubDate><content:encoded>&lt;p&gt;Once you’ve installed Claude Code, it’s time to actually start building a project. But there’s one problem: Claude Code is incredibly powerful, but it knows nothing about your project. It has no idea what technologies you’re using, how your folders are organized, or what rules you follow.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; solves this problem. Place it inside your project folder, and Claude Code will automatically read it every time it starts working, so it understands your project’s context. A well-written &lt;code&gt;CLAUDE.md&lt;/code&gt; makes a huge difference in the quality of the AI’s work.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;why-you-need-a-claudemd&quot;&gt;Why You Need a CLAUDE.md&lt;/h2&gt;
&lt;p&gt;When you first start vibe coding, you’ll run into this experience.&lt;/p&gt;
&lt;p&gt;At first, Claude Code builds exactly the code you want. But as time passes and the project grows, the AI starts adding code in strange ways — using a different library instead of the one you’d already settled on, ignoring your folder structure conventions, or recreating functions that already exist.&lt;/p&gt;
&lt;p&gt;The reason is simple: &lt;strong&gt;most AIs only know what’s in the current chat session.&lt;/strong&gt; Once you start a new conversation, or enough time passes, it forgets the rules and structure you established earlier.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; and other local docs are the most basic and effective way to solve this problem. Claude Code reads these files at the start of every task to understand your project’s current state and rules.&lt;/p&gt;
&lt;p&gt;To do this, create a file named &lt;code&gt;CLAUDE.md&lt;/code&gt; in your project’s top-level folder (the root directory).&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;my-project/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  CLAUDE.md       ← put it here&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  src/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  package.json&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;  ...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When Claude Code runs inside your project folder, it automatically finds and reads this file.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;auto-generating-claudemd-with-the-init-command&quot;&gt;Auto-Generating CLAUDE.md with the &lt;code&gt;/init&lt;/code&gt; Command&lt;/h2&gt;
&lt;p&gt;You don’t actually have to write &lt;code&gt;CLAUDE.md&lt;/code&gt; by hand from scratch. Claude Code has a dedicated command for it: &lt;strong&gt;&lt;code&gt;/init&lt;/code&gt;&lt;/strong&gt;. Whether you’re starting a brand-new project or bringing Claude into one that’s already underway, run Claude Code inside your project folder and enter the command below.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;/init&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you run this command, you’ll see Claude Code scan the files in your current folder — &lt;code&gt;package.json&lt;/code&gt;, the folder structure, config files, and so on — and automatically draft a &lt;code&gt;CLAUDE.md&lt;/code&gt; that fits your project. The basics like tech stack, folder structure, and how to run it get filled in all at once.&lt;/p&gt;
&lt;p&gt;That said, don’t just leave the file &lt;code&gt;/init&lt;/code&gt; produced as-is. Since Claude Code guessed at the content from reading your code alone, it may differ from your actual intent or miss a rule. &lt;code&gt;/init&lt;/code&gt; is only a &lt;strong&gt;convenient starting point&lt;/strong&gt;. You still need to read it over against the “good CLAUDE.md” standards below and refine it — either directly, or by adjusting the specifics through a conversation with Claude Code.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you run &lt;code&gt;/init&lt;/code&gt; again when a &lt;code&gt;CLAUDE.md&lt;/code&gt; already exists, it can take the existing content as a basis, re-examine the project fresh, and improve the document.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-to-include-in-claudemd&quot;&gt;What to Include in CLAUDE.md&lt;/h2&gt;
&lt;p&gt;Judging a good &lt;code&gt;CLAUDE.md&lt;/code&gt; comes down to one question: is it a document detailed enough for a new team member to understand the project? Generally, it includes the following.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Project overview&lt;/strong&gt;
Explain in a sentence or two what kind of service this project is.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Tech stack&lt;/strong&gt;
Specify which languages, frameworks, and libraries you’re using.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Folder structure&lt;/strong&gt;
Explain what each major folder is responsible for.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. How to run it&lt;/strong&gt;
Note how to start the dev server and how to build the project.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. Important rules and cautions&lt;/strong&gt;
Note coding style, files that must never be touched, and anything that requires special care.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;good-claudemd-vs-bad-claudemd&quot;&gt;Good CLAUDE.md vs. Bad CLAUDE.md&lt;/h2&gt;
&lt;p&gt;Let’s compare with real examples.&lt;/p&gt;
&lt;h3 id=&quot;bad-claudemd--too-short-and-abstract&quot;&gt;Bad CLAUDE.md — too short and abstract&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;markdown&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;# My Project&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;A website built with React.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this little information, Claude Code has no way of knowing which version of React you’re using, how state is managed, or how styling is handled. You could still work with this document, but the odds of mistakes are high — which makes it a risk factor for the project’s stability and maintainability.&lt;/p&gt;
&lt;h3 id=&quot;good-claudemd--specific-and-practical&quot;&gt;Good CLAUDE.md — specific and practical&lt;/h3&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;markdown&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;# Project Overview&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;An online book club platform. Users can create clubs, register books, and manage schedules.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;## Tech Stack&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Frontend: React 18, Vite, TailwindCSS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Backend: Node.js 20, Express 4&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Database: PostgreSQL 16&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; State management: Zustand (not Redux)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;## Folder Structure&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;src/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  components/   shared UI components&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  pages/        page-level components&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  hooks/        custom hooks&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  api/          API call functions&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;server/&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  routes/       API routers&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;  data/         DB query functions&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;## How to Run&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;```bash&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Frontend&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;cd&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; src&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x26;&amp;#x26; &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;npm&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; dev&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Backend&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;cd&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; server&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; &amp;#x26;&amp;#x26; &lt;/span&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;npm&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; run&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; dev&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;## Important Rules&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Styling must use TailwindCSS classes only. No inline styles, no adding CSS files.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; API calls must be functions inside &lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;`src/api/`&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;. Do not call fetch directly from components.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; We don&apos;t use TypeScript. JavaScript only.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Always ask for confirmation before installing a new package.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It captures the project’s goals and direction, technical specs, structure, deployment, and cautions. Even if you make changes without knowing the project well, the room for misunderstanding shrinks dramatically, and you can reduce or eliminate critical mistakes — raising the stability of your vibe coding project.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;adjust-to-the-projects-stage-and-nature&quot;&gt;Adjust to the Project’s Stage and Nature&lt;/h2&gt;
&lt;p&gt;If you’re just starting a project and don’t have a structure yet — or it’s a project where you simply can’t fill in every item — it’s fine to describe only what you have ready.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;markdown&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;# Project Overview&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;I want to build a cafe menu recommendation web service.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt;Users pick their mood and the weather, and it recommends a matching drink.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF;font-weight:bold&quot;&gt;## Technical Direction&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Keep it as simple as possible, without leaning on too many libraries&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Build frontend-only, with no backend&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#FFAB70&quot;&gt;-&lt;/span&gt;&lt;span style=&quot;color:#E1E4E8&quot;&gt; Start with HTML, CSS, and vanilla JavaScript&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Like this, the size and scope of the document can vary with the project’s nature and stage. What matters is judging whether it’s well-formed as a kind of &lt;em&gt;handoff&lt;/em&gt; document — one that helps you understand and remember the project.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;updating-claudemd&quot;&gt;Updating CLAUDE.md&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; isn’t a document you write once and forget. You need to update it periodically as the project changes.&lt;/p&gt;
&lt;p&gt;Here are situations that call for an update:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When you adopt a new library or framework&lt;/li&gt;
&lt;li&gt;When your folder structure changes significantly&lt;/li&gt;
&lt;li&gt;When you establish a new coding rule&lt;/li&gt;
&lt;li&gt;When Claude Code keeps making the same mistake (add that mistake as a caution)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That last one matters most. If Claude Code keeps making the same kind of mistake, it’s a sign that there’s a rule that isn’t spelled out in &lt;code&gt;CLAUDE.md&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; is the most fundamental file when building a project together with Claude Code. Write it well, and Claude Code will understand your project’s context and produce consistent code. It’s fine if it’s short — start right now by creating a &lt;code&gt;CLAUDE.md&lt;/code&gt; for your project with just the tech stack and key rules.&lt;/p&gt;
&lt;p&gt;In the next post, we’ll walk step by step through actually building your first screen together with Claude Code.&lt;/p&gt;</content:encoded><category>Vibe Coding for Everyone</category><category>Claude Code</category><category>CLAUDE.md</category><category>Vibe Coding</category></item><item><title>The Common Misconceptions About UX Design</title><link>https://blog.bepuljang.co.kr/en/uxd-manifesto/01/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/uxd-manifesto/01/</guid><description>UX design shows up in job postings, project briefs, and portfolios alike. As familiar as the term is, it is just as often misunderstood — so we start from two of its most common misconceptions and look again.</description><pubDate>Fri, 03 Jul 2026 14:21:42 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;UX design (user experience design).&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It is a term so familiar and so widely used that we hardly think twice about it. We encounter it routinely — in job postings, in project briefs, in design portfolios. And that very familiarity leads us to assume we already understand the term and the concept behind it.&lt;/p&gt;
&lt;p&gt;Yet the moment you look a little closer, UX design turns out to be a field riddled with misunderstandings about its own terminology and concept. As project after project has been carried out on top of this incomplete understanding, the misconceptions have hardened into convention and been reproduced, rather than corrected.&lt;/p&gt;
&lt;p&gt;Even among those we broadly call “experts,” the situation is not much different. Some define UX design loosely as simply “thinking about the user’s experience,” while others assume that once the deliverables of a UX method — a persona, a scenario — are produced, the UX design process has been completed.&lt;/p&gt;
&lt;p&gt;These two views seem unrelated at first glance, but they happen to sit at opposite extremes of the most representative misconception about UX design. One draws the boundaries of UX design so wide that it becomes indistinguishable from virtually any act of design; the other draws them so narrow that it reduces UX design to the production of a particular deliverable. The broad misconception dilutes UX design’s identity; the narrow one confines its spirit within form.&lt;/p&gt;
&lt;p&gt;The essence of UX design lies somewhere between these two extremes. To find it, let’s first look at where each misconception comes from.&lt;/p&gt;
&lt;h2 id=&quot;misconception-1-design-that-thinks-about-the-users-experience&quot;&gt;Misconception 1: “Design that thinks about the user’s experience”&lt;/h2&gt;
&lt;p&gt;To say that UX design “thinks about the user’s experience” is not wrong. It is just too broad. After all, it is rather rare for design of any kind &lt;em&gt;not&lt;/em&gt; to consider the user’s experience.&lt;/p&gt;
&lt;p&gt;From the earliest days of industrial design, designers have thought about forms that fit the hand of the person using the product, colors that appeal to the eye, proportions that suit everyday life. The person designing a chair thought about the comfort of whoever would sit in it; the person designing a poster considered how the viewer’s gaze would move. If this is not thinking about the user’s experience, what is? Follow this line of reasoning and you end up with the equation “design = UX design” — at which point there is no way to explain why we bother attaching the “UX” qualifier and separating it out as its own domain at all.&lt;/p&gt;
&lt;p&gt;But the very fact that the term and concept of UX design were created and are used separately is itself evidence that something clearly distinguishes it from design as it was. To pin down that “something,” we need to go beyond the sweeping declaration of “considering the user” and define more precisely in what way, and at what depth, UX design deals with the user’s experience.&lt;/p&gt;
&lt;h2 id=&quot;misconception-2-design-that-uses-ux-design-methods&quot;&gt;Misconception 2: “Design that uses UX design methods”&lt;/h2&gt;
&lt;p&gt;At the opposite end of the broad definition sits a view that tries to define UX design by its methods — that is, by the form of the deliverables produced through UX design methodology. This one, conversely, is too narrow.&lt;/p&gt;
&lt;p&gt;A methodology is, quite literally, a tool. We may infer the purpose a tool serves from the tool itself, but to equate the tool with the purpose is a plain error. It is the same reason we cannot define the essence of design as marker rendering or Photoshop editing simply because we use markers or Photoshop while designing. Just as handling a marker skillfully amounts to nothing more than a coloring technique without formative thinking behind it, UX design methodology, too, takes on meaning only when the concept and spirit of UX design run through it.&lt;/p&gt;
&lt;p&gt;This danger becomes reality on the ground more often than you would think. Writing a persona document does not mean you have understood the user, and deriving a scenario does not mean you have designed an experience. If these methods are carried out mechanically, without the fundamental questions of UX design — why does this user behave this way, what shapes the experience in this context — then they are nothing but an empty shell that has borrowed only the form.&lt;/p&gt;
&lt;p&gt;Conversely, if you can empathize deeply with the user’s cognitive structure and behavioral patterns and reflect that consistently in your design decisions — even without any fixed method or format — that is precisely what it means to take a UX design approach.&lt;/p&gt;
&lt;h2 id=&quot;so-what-is-ux-design&quot;&gt;So What Is UX Design?&lt;/h2&gt;
&lt;p&gt;We have looked at two misconceptions. Define it too broadly and UX design dissolves into all of design, losing any meaning of its own; define it too narrowly and it shrinks to the performance of a particular method. So how, exactly, can UX design be defined?&lt;/p&gt;
&lt;p&gt;It would be nice to answer that question cleanly in a single sentence, but a hasty one-liner can become the seed of yet another misconception. So rather than offering one clear-cut definition, this series proposes the journey of arriving at the answer itself. We will work through it one step at a time: the era and the problems out of which UX design emerged, how it declared itself distinct from the existing design tradition, and how the “experience” it centers on is formed within the human mind and can be deliberately shaped.&lt;/p&gt;
&lt;p&gt;This journey runs along three broad currents. First, we look at why the declaration of UX design was necessary, within its historical context. Next, we go into the fundamental principles that make up experience — understanding how the user’s cognition works, by what mechanism behavior takes shape, and how emotion influences judgment. Finally, building on that understanding, we cover the procedures and tools for observing, analyzing, and designing the user’s experience in an actual design process.&lt;/p&gt;
&lt;p&gt;What this series ultimately wants to talk about is not tools but perspective, not technique but spirit. Concrete tools and techniques will of course appear along the way, but they are only the channels through which perspective and spirit are realized — never ends in themselves. Understanding why a tool is needed before learning it, and knowing what a method is trying to capture before applying it — this is the order of learning UX design that this series proposes.&lt;/p&gt;
&lt;p&gt;In the next post, as the starting point of this journey, we trace from the history of design itself just what era and circumstances gave rise to UX design as a “declaration.”&lt;/p&gt;</content:encoded><category>The UX Design Manifesto</category><category>UX Design</category><category>User Experience</category><category>Design</category></item><item><title>How to Install Claude Code — From Terminal to Your First Conversation</title><link>https://blog.bepuljang.co.kr/en/vibecoding/02/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/vibecoding/02/</guid><description>A step-by-step guide to installing Claude Code for the first time, covering terminal basics, Node.js installation, logging in (with a subscription or API key), and sending your first prompt.</description><pubDate>Fri, 03 Jul 2026 13:49:41 GMT</pubDate><content:encoded>&lt;p&gt;To start vibe coding, the first thing you need to do is install a tool. This post walks you through installing Claude Code for the first time, step by step, from basic terminal usage all the way to your first conversation. Every step is explained in as much detail as possible so you can follow along even without any development experience.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-is-a-terminal&quot;&gt;What is a terminal&lt;/h2&gt;
&lt;p&gt;A terminal — literally an “end” or “terminus,” as the word itself suggests — is the outermost window through which a computer communicates with the outside world. The term goes back to computing history: it originated from the communication devices used to access mainframe computers so large they could fill a room.&lt;/p&gt;
&lt;p&gt;A modern terminal refers to a tool that lets you perform tasks by typing text commands into your computer. Instead of clicking icons with a mouse, you type commands on the keyboard to install or run programs. This is called a CLI (Command Line Interface).&lt;/p&gt;
&lt;p&gt;It might feel unfamiliar at first, but once you get comfortable with the CLI it becomes very convenient — especially for vibe coding — because the right commands let you carry out a great deal of work with ease. Since AI services can smooth over these parts for you, it’s far faster and easier than hunting through a GUI (Graphic User Interface) to find and run a task. Copy the code commands in this post and use them as-is, and you’ll feel these advantages firsthand.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How to open a terminal&lt;/strong&gt;&lt;/p&gt;

















&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;OS&lt;/th&gt;&lt;th&gt;Method&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;macOS&lt;/td&gt;&lt;td&gt;Open Spotlight with &lt;code&gt;Command + Space&lt;/code&gt; → type “Terminal” → press Enter&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Windows&lt;/td&gt;&lt;td&gt;Press &lt;code&gt;Windows + R&lt;/code&gt; → type &lt;code&gt;cmd&lt;/code&gt; → press Enter (or search for PowerShell)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Once you open the terminal, you’ll see a screen like the one below. If the cursor is blinking, it’s ready for you to type a command.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;username@computername ~ %&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;prerequisite-installing-nodejs&quot;&gt;Prerequisite: Installing Node.js&lt;/h2&gt;
&lt;p&gt;Claude Code runs in a Node.js environment. First, check whether Node.js is already installed.&lt;/p&gt;
&lt;p&gt;Type the command below into the terminal and press Enter.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;node&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If a version number like &lt;code&gt;v20.0.0&lt;/code&gt; appears, it’s already installed. If nothing appears or you get an error, you’ll need to install Node.js.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How to install Node.js&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Go to &lt;a href=&quot;https://nodejs.org&quot;&gt;nodejs.org&lt;/a&gt; and download the LTS version (Long Term Support, the stable version). Run the installer and complete it with the default settings.&lt;/p&gt;
&lt;p&gt;After installing, open a new terminal window and run &lt;code&gt;node --version&lt;/code&gt; again — if a version number shows up, everything is set up correctly.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;installing-claude-code&quot;&gt;Installing Claude Code&lt;/h2&gt;
&lt;p&gt;Once Node.js is ready, install Claude Code. Type the command below into the terminal.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;npm&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; install&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; -g&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; @anthropic-ai/claude-code&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;-g&lt;/code&gt; flag stands for “global,” meaning it’s installed system-wide so you can use it from anywhere on your computer. In other words, a program installed this way can be run from any folder going forward. The installation takes about 1-2 minutes.&lt;/p&gt;
&lt;p&gt;Once it’s done, verify it with the command below.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;claude&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt; --version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If a version number is printed, it’s installed correctly.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;choosing-how-to-log-in-subscription-or-api-key&quot;&gt;Choosing How to Log In: Subscription or API Key&lt;/h2&gt;
&lt;p&gt;To use Claude Code, you need to log in. There are two main ways to do this, and for most individual users, the first is recommended.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Option 1: Log in with a Claude subscription (recommended)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you’re on a Claude subscription plan like Pro or Max, you can log in with that account directly — no separate API key or credit top-up required. Your usage is handled within your subscription plan’s limits, so you pay a flat monthly rate and don’t have to worry about what each session costs. If you don’t have a subscription yet, you can check the plans at &lt;a href=&quot;https://claude.ai&quot;&gt;claude.ai&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Option 2: Use an Anthropic API key&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you’d rather pay as you go instead of subscribing, you can create and use an API key.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Visit &lt;a href=&quot;https://console.anthropic.com&quot;&gt;console.anthropic.com&lt;/a&gt;, create an account, and log in.&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;API Keys&lt;/code&gt; from the left-hand menu, then click &lt;code&gt;Create Key&lt;/code&gt; to create one. The key is a long string that starts with &lt;code&gt;sk-ant-&lt;/code&gt;, and it’s shown only once right after creation, so copy it somewhere safe.&lt;/li&gt;
&lt;li&gt;Add credits from the &lt;code&gt;Billing&lt;/code&gt; menu. If you’re just starting out, $5-10 will cover quite a lot of light vibe coding work.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;The API key route bills you for the tokens you use, while subscription login is a flat rate that makes costs easy to predict. For a beginner who isn’t doing heavy work, neither option is much of a burden.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&quot;running-claude-code-for-the-first-time&quot;&gt;Running Claude Code for the first time&lt;/h2&gt;
&lt;p&gt;In the terminal, navigate to the folder you want to work in, then run Claude Code.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#B392F0&quot;&gt;claude&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first time you run it, you’ll see a screen asking you to choose a login method. To log in with a subscription, choose the Claude account login — a web browser will open, and you sign in with your account to finish authenticating. If you’ve decided to use an API key, choose that option and paste the key you copied earlier, then press Enter.&lt;/p&gt;
&lt;p&gt;Once you’re logged in, you’ll see a screen like this.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Claude Code&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;&gt; &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the cursor is blinking after the &lt;code&gt;&gt;&lt;/code&gt;, you’re ready to go.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;sending-your-first-prompt&quot;&gt;Sending your first prompt&lt;/h2&gt;
&lt;p&gt;Let’s start your first conversation with Claude Code. Type the following after the &lt;code&gt;&gt;&lt;/code&gt; and press Enter.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;plaintext&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;Please create a simple HTML file. &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;The title should be &quot;Hello, Vibe Coding,&quot; and it should display large, centered text &lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span&gt;that says &quot;My first page made with AI.&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If a permission prompt appears when Claude Code tries to create the file, approve it and press Enter.&lt;/p&gt;
&lt;p&gt;A moment later, an &lt;code&gt;.html&lt;/code&gt; file will be created. Open this file in your browser, and you’ll see the page you just requested. You’ve just succeeded at your very first bit of vibe coding.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;frequently-used-claude-code-commands&quot;&gt;Frequently used Claude Code commands&lt;/h2&gt;
&lt;p&gt;Here are some basic commands that are useful to know while using Claude Code.&lt;/p&gt;





































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Command&lt;/th&gt;&lt;th&gt;Description&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;claude&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Run Claude Code&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/help&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Show the list of available commands&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/clear&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Clear the conversation history&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/compact&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Summarize the conversation history&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/mcp&lt;/code&gt;&lt;/td&gt;&lt;td&gt;List MCPs (Model Context Protocols)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;/exit&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Quit Claude Code&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;Ctrl + C&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Stop the current task&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Of these, &lt;code&gt;/compact&lt;/code&gt; compresses the conversation so far to save the tokens consumed over the rest of the session. Used too often, it keeps cutting your context short and gets in the way, but used in moderation it’s a big help for managing token usage. We’ll come back to &lt;code&gt;/mcp&lt;/code&gt; later, when we introduce MCP.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;how-to-navigate-between-folders&quot;&gt;How to navigate between folders&lt;/h2&gt;
&lt;p&gt;To move to a specific project folder in the terminal, use the &lt;code&gt;cd&lt;/code&gt; command.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;cd&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; folder-path&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For example, if you created a folder called &lt;code&gt;my-project&lt;/code&gt; on your desktop and want to work inside it, you’d type the following.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# macOS&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;cd&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; ~/Desktop/my-project&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Windows&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;cd&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt; C:&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\U&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;sers&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\u&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;sername&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\D&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;esktop&lt;/span&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;\m&lt;/span&gt;&lt;span style=&quot;color:#9ECBFF&quot;&gt;y-project&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To check which folder you’re currently in, use the command below.&lt;/p&gt;
&lt;pre class=&quot;astro-code github-dark&quot; style=&quot;background-color:#24292e;color:#e1e4e8; overflow-x: auto;&quot; tabindex=&quot;0&quot; data-language=&quot;bash&quot;&gt;&lt;code&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# macOS / Linux&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;pwd&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#6A737D&quot;&gt;# Windows&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;color:#79B8FF&quot;&gt;cd&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;
&lt;p&gt;So far, we’ve opened the terminal, installed Node.js and Claude Code, gotten an API key, and sent our first prompt.&lt;/p&gt;
&lt;p&gt;In the next post, we’ll cover how to write a &lt;code&gt;CLAUDE.md&lt;/code&gt; file so that Claude Code properly understands your project. This one file can make a huge difference in the quality of the AI’s work.&lt;/p&gt;</content:encoded><category>Vibe Coding for Everyone</category><category>Claude Code</category><category>Installation</category><category>Terminal</category><category>Vibe Coding</category></item><item><title>What Is Vibe Coding?</title><link>https://blog.bepuljang.co.kr/en/vibecoding/01/</link><guid isPermaLink="true">https://blog.bepuljang.co.kr/en/vibecoding/01/</guid><description>A look at vibe coding, where AI writes the code for you: what it is, when it&apos;s a good fit, and how popular tools like Cursor, Claude Code, and Copilot compare.</description><pubDate>Thu, 02 Jul 2026 06:29:52 GMT</pubDate><content:encoded>&lt;p&gt;Have you heard that the era of AI writing code for you has arrived? The term “vibe coding” has been spreading fast lately. It refers to coding centered on the vibe, or feel, rather than the logic and structure that traditional hands-on development emphasized — a term that emerged as AI services capable of understanding natural language and translating it directly into code came onto the scene.&lt;/p&gt;
&lt;p&gt;In other words, instead of writing code yourself, you build software by having a conversation with an AI — and that means “even people with no development experience can build real, working products.”&lt;/p&gt;
&lt;p&gt;This post breaks down exactly what vibe coding is, what tools are available, and how you can get started.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;what-is-vibe-coding&quot;&gt;What Is Vibe Coding?&lt;/h2&gt;
&lt;p&gt;AI researcher Andrej Karpathy, credited with first coining the term “vibe coding,” described his own workflow like this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“I fully give in to the vibes, embrace exponentials, and forget that the code even exists… I just see stuff, say stuff, run stuff, and copy paste stuff, and it mostly works.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In other words, you focus &lt;strong&gt;less on reading and understanding code, and more on describing to the AI what you want to build&lt;/strong&gt; — investing the cognitive effort that used to go into writing code into the “feel” of building a better product instead.&lt;/p&gt;
&lt;p&gt;Here’s how it compares to traditional development:&lt;/p&gt;



































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Aspect&lt;/th&gt;&lt;th&gt;Traditional Development&lt;/th&gt;&lt;th&gt;Vibe Coding&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Main activity&lt;/td&gt;&lt;td&gt;Writing code yourself&lt;/td&gt;&lt;td&gt;Instructing AI through prompts&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Required knowledge&lt;/td&gt;&lt;td&gt;Programming languages, algorithms&lt;/td&gt;&lt;td&gt;Ability to clearly describe what you want&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Speed&lt;/td&gt;&lt;td&gt;Implementation speed depends on your knowledge&lt;/td&gt;&lt;td&gt;Ideas can be turned into code quickly&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Handling errors&lt;/td&gt;&lt;td&gt;Debugging it yourself&lt;/td&gt;&lt;td&gt;Handing the error message to the AI&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Barrier to entry&lt;/td&gt;&lt;td&gt;High&lt;/td&gt;&lt;td&gt;Low&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;What made vibe coding possible is the huge improvement in how well large language models like GPT-4, Claude, and Gemini understand code. AI has moved beyond generating small code snippets — it can now understand a project’s overall structure and write code that fits the context.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;who-is-vibe-coding-actually-useful-for&quot;&gt;Who Is Vibe Coding Actually Useful For?&lt;/h2&gt;
&lt;p&gt;As described above, the essence of vibe coding lies in someone who already has a certain level of service-building capability investing their skill and energy into the “vibe” to create better products more efficiently. So rather than working or being useful in every case, vibe coding can be a great fit in some situations and hit its limits in others, as shown below.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Vibe coding works well when:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You want to turn an idea into a prototype quickly&lt;/li&gt;
&lt;li&gt;You have no development experience but want to build a real product yourself&lt;/li&gt;
&lt;li&gt;You want to speed through repetitive implementation work&lt;/li&gt;
&lt;li&gt;You want to quickly validate a side project or an MVP&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Vibe coding shows its limits when:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You’re building a system where security is critical&lt;/li&gt;
&lt;li&gt;You’re building a large-scale service where performance optimization is essential&lt;/li&gt;
&lt;li&gt;The work requires designing complex algorithms&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In short, vibe coding is a powerful tool for quickly building and validating ideas. You might think it lets you build and run a complete service with no knowledge or experience at all, but in reality it’s simply a far more convenient tool than what came before — the important knowledge and skills still have to come from you.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;a-comparison-of-popular-vibe-coding-tools&quot;&gt;A Comparison of Popular Vibe Coding Tools&lt;/h2&gt;
&lt;p&gt;There’s a wide range of AI coding tools on the market today. Understanding what makes each one different will help you pick the right one for your situation.&lt;/p&gt;





























































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Tool&lt;/th&gt;&lt;th&gt;Type&lt;/th&gt;&lt;th&gt;Features&lt;/th&gt;&lt;th&gt;Pricing&lt;/th&gt;&lt;th&gt;Best For&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Cursor&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;IDE&lt;/td&gt;&lt;td&gt;VS Code-based editor. Understands and edits the entire codebase&lt;/td&gt;&lt;td&gt;$20/month (free plan available)&lt;/td&gt;&lt;td&gt;Existing developers, larger projects&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Claude Code&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;CLI&lt;/td&gt;&lt;td&gt;Terminal-based. Full file system access, MCP integration support&lt;/td&gt;&lt;td&gt;Pay-per-API-usage&lt;/td&gt;&lt;td&gt;Automation, complex projects&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;GitHub Copilot&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;IDE plugin&lt;/td&gt;&lt;td&gt;Autocomplete-focused support in VS Code, JetBrains, and more&lt;/td&gt;&lt;td&gt;$10–19/month&lt;/td&gt;&lt;td&gt;Developers who want AI assistance without leaving their current setup&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Windsurf&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;IDE&lt;/td&gt;&lt;td&gt;Similar to Cursor. Built on Codeium&lt;/td&gt;&lt;td&gt;Free plan available&lt;/td&gt;&lt;td&gt;Beginners who want to start without any cost&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Bolt.new&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Web&lt;/td&gt;&lt;td&gt;Works directly in the browser. No installation needed, deployment included&lt;/td&gt;&lt;td&gt;Free plan available&lt;/td&gt;&lt;td&gt;Absolute beginners who want to start with zero setup&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;v0 (Vercel)&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Web&lt;/td&gt;&lt;td&gt;Specialized for generating UI components. Focused on React/Next.js&lt;/td&gt;&lt;td&gt;Free plan available&lt;/td&gt;&lt;td&gt;Quickly building a frontend UI&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Replit Agent&lt;/strong&gt;&lt;/td&gt;&lt;td&gt;Web&lt;/td&gt;&lt;td&gt;Handles everything from writing code to running and deploying it, all in the browser&lt;/td&gt;&lt;td&gt;Paid&lt;/td&gt;&lt;td&gt;Experiencing the full workflow with no environment setup&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h3 id=&quot;a-closer-look-at-each-tool&quot;&gt;A Closer Look at Each Tool&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Cursor&lt;/strong&gt;
Currently the most widely used vibe coding tool. Built on top of VS Code, it offers an experience similar to a familiar code editor. The AI understands your entire project’s code, tracks the relationships between files, and edits accordingly. It’s a good fit for people who already have development experience or who are working on larger projects.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Claude Code&lt;/strong&gt;
A terminal-based AI coding tool made by Anthropic. Instead of running inside an IDE, it operates from the terminal (command line) and can access your whole system — reading, writing, and deleting files as needed. One standout feature is its support for MCP (Model Context Protocol), which lets it connect with external services. It particularly shines when handling automation tasks or complex project structures.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GitHub Copilot&lt;/strong&gt;
An AI coding assistant made by GitHub and Microsoft. It installs as a plugin into editors like VS Code, JetBrains, and Vim. It’s a great fit for developers who want to keep using their current editor setup while adding AI-powered autocomplete on top.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bolt.new&lt;/strong&gt;
A tool you can use directly in your browser with no installation required. It handles everything from creating a project to deploying it, all inside the browser. Since there’s no setup barrier at all, it’s a great way for complete beginners to try vibe coding for the first time.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;this-series-centers-on-claude-code&quot;&gt;This Series Centers on Claude Code&lt;/h2&gt;
&lt;p&gt;There are many vibe coding tools out there, each with its own strengths and weaknesses. This series will use &lt;strong&gt;Claude Code&lt;/strong&gt; as its reference tool.&lt;/p&gt;
&lt;p&gt;We chose Claude Code because it’s terminal-based, which means you have direct control over your environment; it supports MCP integration, so it can connect with external tools; and it has access to your entire file system, so it can handle even complex project structures. We selected it as the tool best suited for coding purely on the “vibe” that is the essence of vibe coding.&lt;/p&gt;
&lt;p&gt;In the next post, we’ll walk through how to actually install Claude Code, starting from the basics of using a terminal.&lt;/p&gt;</content:encoded><category>Vibe Coding for Everyone</category><category>vibe coding</category><category>AI coding</category><category>Claude Code</category><category>Cursor</category></item></channel></rss>