<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Team QANDA - Medium]]></title>
        <description><![CDATA[전 세계 9천만 학생이 사용하는 교육 플랫폼 콴다를 만듭니다. - Medium]]></description>
        <link>https://blog.mathpresso.com?source=rss----35e40785d286---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Team QANDA - Medium</title>
            <link>https://blog.mathpresso.com?source=rss----35e40785d286---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 31 Oct 2026 15:49:35 GMT</lastBuildDate>
        <atom:link href="https://blog.mathpresso.com/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[How Was the New QANDA Agent Built?]]></title>
            <link>https://blog.mathpresso.com/how-was-the-new-qanda-agent-built-73f66934f006?source=rss----35e40785d286---4</link>
            <guid isPermaLink="false">https://medium.com/p/73f66934f006</guid>
            <category><![CDATA[febe]]></category>
            <dc:creator><![CDATA[Dave Kwon]]></dc:creator>
            <pubDate>Thu, 07 Aug 2025 06:58:40 GMT</pubDate>
            <atom:updated>2025-08-07T06:59:54.208Z</atom:updated>
            <content:encoded><![CDATA[<h3>The New QANDA Agent Is Here</h3><h4>Designing a smarter, more powerful Agent</h4><p>Hello, I’m Dave, a Backend Engineer at QANDA.</p><p>Some of you may have been surprised by the recent changes on QANDA. In response to the era of Generative AI, we’ve gone beyond simple feature upgrades to conduct a full redesign — ranging from the main screen of the app to the overall user journey.</p><p>In this blog post, we’ll walk you through:</p><ul><li>The new direction of QANDA’s AI product in the Gen AI era</li><li>The technical approaches our backend team took to bring that vision to life</li></ul><p>We hope this provides practical insights for anyone interested in the design and architecture of AI-powered services.</p><h4><strong>Why and How Has QANDA Changed?</strong></h4><p>The previous version of the QANDA app was best known for its core feature: allowing students to take a photo of a question they didn’t understand and receive an AI-generated solution.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/720/1*S7tmT6CuV-Iib2jS_8nHig.png" /></figure><p>However, this feature was heavily focused on a specific use case — providing solutions when a student encounters a difficult problem.<br>As a result, it had limitations when it came to supporting a broader range of learning goals, such as concept explanations, recommending similar problems, or generating quizzes.</p><p><strong>🗺 A Newly Defined Direction</strong></p><p>So, we set a new goal:</p><blockquote>“Understand the user’s learning intent in real time, and leverage LLMs to deliver optimal UI/UX accordingly.”</blockquote><p>With this new direction, QANDA has been transformed from a simple problem-solving assistant into a <strong>proactive AI learning assistant</strong> capable of supporting a wide range of educational needs.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lh33zMh_5wFkNZPUUqswlg.png" /></figure><h4><strong>A Completely New Engineering Paradigm</strong></h4><p>At its core, the new system we built was what’s commonly referred to as an <strong>“Agent.”</strong> <br>Instead of simply reacting based on predefined logic, we needed a structure that could understand a wide range of user requests and intelligently select the appropriate tool to solve the problem.</p><p>Initially, we followed a typical product development process: listing out user needs, defining features for each, and then implementing them in the UI. For example:</p><ul><li>“I don’t know how to solve this problem.” → Provide step-by-step solution</li><li>“I solved it myself — can you check if it’s correct?” → Review and give feedback on handwritten answers</li><li>“I don’t understand the concept.” → Explain the concept with examples</li><li>“I want to practice similar questions.” → Search and recommend similar problems</li><li>“I think I’m starting to get it.” → Generate a quiz to check understanding</li></ul><p>In a traditional setup, each of these requests would have resulted in a separate API and dedicated UI components to match.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Wqx0bKwtZ4c2EfY2FpEN3w.png" /></figure><p>However, we chose a different approach: a unified Agent that <strong>drives the interaction</strong> with students through <strong>a single chat-based interface</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3yW4XXbLCyMwUeQXNUPeAg.png" /></figure><p>In other words, our goal was to build a structure capable of handling diverse user needs within a single conversational flow. To make this possible, there were three key technical challenges we had to carefully consider:</p><p>✅ <strong>1. Defining Chat Interface Components</strong><br> → The system needed to dynamically combine different blocks based on user requests.</p><p>✅ <strong>2. Agent Intent Analysis</strong><br> → The Agent had to understand the user’s learning intent from natural language input.</p><p>✅ <strong>3. Designing a Tool Framework for the Agent</strong><br> → The Agent had to be capable of solving complex tasks by combining multiple tools.</p><p>With these principles in place, we were able to abstract the entire system into the following three core concepts:</p><blockquote><strong>Agent (Intent Understanding) → Tool (Problem Solving) → Block (Visual Response)</strong></blockquote><h4><strong>Understanding Student Intent</strong></h4><p>As mentioned earlier, accurately <strong>identifying the student’s intent</strong> is critical for QANDA to flexibly support a wide range of learning use cases.</p><p>For example, even when a student uploads the same photo of a problem, their intent can vary greatly:</p><ul><li>They may simply want a solution</li><li>They might want feedback on their own answer</li><li>They could be looking for a concept explanation</li><li>They may want to practice with similar problems</li><li>They might want a quiz generated based on the content</li></ul><p>These diverse needs make intent recognition essential. However, in practice, it’s challenging for current LLMs to <strong>perfectly classify all of these intents in a single shot</strong>.</p><p>Moreover, as new learning needs and use cases will continue to emerge, it was essential to design the system <strong>from the beginning with scalability and flexibility in mind</strong>.</p><p><strong>🤖 A Multi-Agent Strategy for Intent Analysis and Execution</strong></p><p>To address this, we first divided user needs into two main flows:</p><ul><li><strong>Problem-Solving-Oriented:</strong> Flows focused on helping students solve problems, such as feedback, explanations, and concept clarifications</li><li><strong>Content-Creation-Oriented:</strong> Flows focused on generating learning materials, such as quizzes, flashcards, and summary notes</li></ul><p>Based on this separation, we decided to build independent Agents responsible for each flow. This led us to the following architecture:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2b4YmlRBw-G-Id1o4WUQ1Q.png" /></figure><p><strong>🕸️ Multi-Agent Swarm Architecture</strong></p><p>This architecture resembles what is commonly known as a <strong><em>Swarm Architecture</em></strong>. Each agent focuses on its <strong>specific domain</strong> of expertise, and requests that fall outside its scope are <strong>handed off</strong> to another appropriate agent. (<a href="https://langchain-ai.github.io/langgraph/agents/multi-agent/#swarm">reference</a>)</p><p>The initial request is processed by the determine_active_agent, which delegates the responsibility to either the <strong>Answer Question Agent</strong> (focused on problem solving) or the <strong>Generate Learning Material Agent</strong> (focused on content creation).<br>From that point on, the active agent handles the conversation until the context changes, at which point it hands off the session to another agent as needed.</p><p>(For reference: the info_agent handles QANDA policy-related questions, while the vanilla_agent is a free-tier agent for users who have exhausted their quota.)</p><p>Key advantages of this approach include:</p><ul><li>✅ <strong>Clearer context management</strong><br> → Each agent only needs to know its own role, which minimizes prompt complexity and history tracking.</li><li>✅ <strong>Use-case-specific evaluation and testing</strong><br> → Evaluation metrics and test scenarios can be independently designed for each use case.</li><li>✅ <strong>Separation of concerns for focused development</strong><br> → Developers can work with clear goals and responsibilities, as each agent has a well-defined purpose.</li><li>✅ <strong>High scalability and reusability of agents</strong><br> → New use cases can be supported by adding or combining independent agents.<br> → The same agent can be reused across different scenarios.</li></ul><p>Of course, like any architectural decision, there are trade-offs. In this case, the Swarm architecture can increase time and cost to reach the final output. We’ll cover how we addressed that challenge in a future post.</p><p>Next, we’ll dive into the specific roles and characteristics of each agent.</p><h4><strong>Problem-Solving Agent: Planning and Execution</strong></h4><p>The first component we began developing was the <strong>Problem-Solving Agent</strong>. This agent needed to address a wide range of learning needs, including:</p><ul><li><strong>Step-by-step solutions</strong> for questions students don’t understand</li><li><strong>Similar question recommendations</strong> by analyzing question types and retrieving relevant problems from QANDA’s database</li><li><strong>Feedback on handwritten answers</strong>, reviewing and commenting on students’ own solutions</li><li><strong>Graph generation</strong>, to visually represent functions or geometric concepts when needed</li><li><strong>Handling multiple questions</strong> in a single image, separating and solving them one by one</li><li><strong>Terminology explanations</strong> for concepts that appear during the solution process</li></ul><p>There were also system-level goals and constraints to consider:</p><ul><li>Maintain a <strong>minimum answer accuracy rate</strong> (e.g., n%)</li><li><strong>Optimize cost</strong> by increasing the use of database-based question resources</li></ul><p><strong>🧠 Initial Approach: ReAct-Based Architecture</strong></p><p>Our initial design imagined the system as a set of <strong>modular Tools</strong>, with the Agent selecting and calling the appropriate Tool depending on the context of the user’s query. This approach closely resembled the <strong><em>ReAct</em></strong> (Reasoning + Acting) strategy (<a href="https://www.ibm.com/think/topics/react-agent">reference</a>), and followed a flow like this:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LSNpJNaACDHGeB2N8HaMzw.png" /><figcaption>Simple ReAct Agent</figcaption></figure><p>However, once we began actual evaluations, several limitations became apparent:</p><p>There was a lack of overall coherence in responses, and in particular, the system often lost context or <strong>mixed up the order</strong> when handling <strong>multiple questions</strong> within a single request.</p><p>For example, when a user submitted an image with multiple problems and asked the agent to solve two of them, we frequently observed issues like the one below:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/814/1*zldh_bLtrYYgj8sfoguiMA.png" /></figure><p>The expected behavior was for the agent to solve questions a) and b) in the first turn, then continue with questions c) and d) in the next. However, we often saw issues such as the agent re-solving questions a) and b), or skipping question c) entirely and jumping to d).</p><p>Additionally, the agent was supposed to first consult the QANDA database and return a matching problem if one existed. But in practice, it often displayed <strong>a greedy tendency</strong> — focusing only on the immediate problem in front of it, without considering the broader context.</p><p>Of course, if the task were purely solving a single question, the agent would have performed much better. However, since the Problem-Solving Agent is expected to handle a variety of complex and layered requests, we needed a different approach.</p><p>👉 <strong>Strategy Shift: Introducing a Plan-and-Execute Architecture</strong></p><p>As a result, we moved away from the ReAct-style approach that relied on reactive tool invocation, and instead adopted a <strong>Plan → Execute</strong> architecture. (r<a href="https://langchain-ai.github.io/langgraph/tutorials/plan-and-execute/plan-and-execute/">eference</a>)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*M5NSMOl9S3U6jsmMMbCueg.png" /></figure><p>In the <strong>Plan</strong> phase, the system analyzes the overall structure of the request and outlines the necessary tasks in order. Then, in the <strong>Execute</strong> phase, it follows that plan step by step — calling the appropriate tools and updating the system state along the way.</p><p>The <strong>Replan</strong> phase is optional and comes into play after task execution, where the system can adjust the plan based on the intermediate results.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*uwrAmzv3zdrV8NRMaOpaJQ.png" /></figure><p>The diagram may look a bit complex, but it reflects the actual architecture of our production Problem-Solving Agent. As described above, the agent completes the problem-solving process by going through the <strong>Plan</strong>, <strong>Execute</strong>, and optionally <strong>Replan</strong> phases.</p><p>This approach has shown much more stable performance when handling <strong>multi-step problems, sequential requests, and generating complex responses</strong>. Because the agent is now able to think in a more structured way, it can deliver responses that are more coherent and consistent.<br> The previous issue of “greedy” behavior was also resolved effectively — by introducing the <strong>Replan</strong> phase, the agent can now check for duplicate or already-answered questions before proceeding.</p><p><strong>🦾 QANDA’s Problem-Solving Assistant in Action</strong><br>Let’s take a look at how the newly built Problem-Solving Agent powers real features in our product, supporting students in learning through problem data.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UTf4pjUNuCZdepGcgWM1Pg.png" /></figure><p>The image above shows the result of asking a question about a geometric sequence. The agent first created a <strong>plan</strong> to provide feedback based on the user’s handwritten solution, and then used the <strong>feedback tool</strong> to <strong>execute</strong> that plan — delivering a corrected and annotated response.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sLHpfqlXPzMCZA3zgTLhNQ.png" /></figure><p>Looking at the flowchart above may make things easier to understand — you can see how the <strong>Planner</strong> analyzes the user’s request, decides which <strong>Tool</strong> to use, and passes that plan to the <strong>Executor</strong>.</p><p>In contrast,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Ri1wDaKeuz-3ABmmxdRtoQ.png" /></figure><p>If the user, using the same image, also wants to search for similar solutions,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FlN5y4R1XMMffmwh3XpyCA.png" /></figure><p>As shown in the flowchart above, if that’s the case, the <strong>Planner</strong> can include the <strong>search tool</strong> — which queries the QANDA database — as part of its plan. Then, the <strong>Executor</strong> follows through by executing the search and returning a matching problem.</p><p>In reality, the Planner handles a wide variety of use cases, making its role even more critical. To support this, we’ve implemented a number of carefully designed guideline prompts within QANDA to help steer the Planner’s behavior effectively.</p><p>In this way, the <strong>Problem-Solving Agent</strong> was the first to be designed within the QANDA system — and also the one that handles the broadest range of scenarios. As such, it served not only as a core product feature but also as a testbed for validating key Agent design concepts such as reasoning, tool usage, and handling user requests.</p><p>While building the Problem-Solving Agent, the QANDA Backend Team explored and evaluated a variety of architectural approaches, including <strong>ReAct</strong> and <strong>Plan-and-Execute</strong> frameworks. Through this, we accumulated valuable knowledge about which structures are best suited for different types of tasks.</p><p>We also addressed many practical challenges — such as how to handle failures in the agent flow, how to separate internal messages to the model from the user-facing responses, and more.</p><p>The insights we gained through this process became a solid foundation when we moved on to develop the <strong>Generator Agent</strong>, which powers content creation workflows.<br> Next, we’ll introduce how this agent was designed to go beyond solving problems and proactively assist students in their learning journey.</p><h4><strong>Content Generation Agent: Creating Learning Materials</strong></h4><p>While the Problem-Solving Agent focuses on the <em>process of finding correct answers</em>, the <strong>Content Generation Agent</strong> is responsible for shaping the learning journey — helping learners engage actively and enhance their understanding through interactive experiences.</p><p>For example, the process of generating quizzes or flashcards doesn’t end with a single response:</p><ul><li>It <strong>takes more time</strong> to generate the learning content itself,</li><li>And it involves multiple follow-up interactions such as <strong>solving the quiz, grading, regenerating, or editing,</strong></li><li>All of which require a <strong>dedicated UI</strong> and flow that goes beyond a traditional chat interface.</li></ul><p>When designing for this kind of interaction, many LLM-based product ecosystems refer to these elements as <strong>Canvas</strong> or <strong>Artifact</strong> — distinct components defined outside the standard chat paradigm.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*aN2i_cxrr2usNjpm_snsgQ.png" /></figure><p>We also adopted the term <strong>“Artifact”</strong> within QANDA to clearly define and separate these content-centric experiences from standard chat flows at the system level.</p><p><strong>🎛️ What is an Artifact?</strong></p><p>An <strong>Artifact</strong> is not just a single response block. It represents an independent learning experience that takes place in <strong>a separate viewer interface</strong>, where the <strong>user can interact directly</strong> — and those interactions influence both system state and future AI responses.</p><p>Key characteristics of an Artifact include:</p><ul><li>Users can <strong>interact within a dedicated viewer</strong> and directly modify information.</li><li>These changes <strong>affect the context of subsequent AI responses</strong>.<br>For example: the feedback and suggestions an LLM can give will differ greatly depending on whether the user hasn’t taken a quiz yet, or has already completed and reviewed it.</li><li>To support this behavior, an Artifact — unlike a normal response — requires backend logic for <strong>state management, data persistence, change tracking</strong>, and sometimes even<strong> </strong>needs to be <strong>managed by its own dedicated service</strong>.</li></ul><p><strong>❓When Does Something Qualify as an Artifact?</strong></p><p>From a development perspective, we typically classify a feature as an Artifact if it meets one or more of the following criteria:</p><ul><li>Events occur beyond simple <strong>Read</strong> operations — e.g., the user’s actions result in changes to system state (Create/Update/Delete).</li><li>Session state or results need to be persistently stored.</li><li>The interaction cannot be handled purely through chat, and requires a <strong>dedicated UI or task flow</strong>.</li><li>Examples include: Taking a quiz and getting graded, viewing and editing handwritten notes, etc.</li></ul><p>An Artifact is not just a visual element — it’s a branching point in the user experience where <strong>data changes</strong>, <strong>system context shifts</strong>, and <strong>interactive learning happens</strong>.</p><p>The <strong>Generator Agent</strong> is designed to lead these Artifact-driven experiences. It serves as a proactive learning assistant that encourages students to participate more actively in their learning journey.</p><p><strong>🧮 How QANDA Generates Artifacts</strong></p><p>With this definition in place, we developed the <strong>Generator Agent</strong> and the architecture for its interaction patterns, which are shown below:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hoJ44wu--TdO8bcYSqtn9w.png" /></figure><p>First, regarding the structure of the agent — we also used a <strong>Plan-and-Execute</strong> approach here, since the agent needed to support a variety of interactions such as creating and modifying artifacts.</p><p>One key difference, however, is that we did <strong>not</strong> include a <strong>Replan</strong> phase, as there were no complex conditional flows or branching logic requiring dynamic replanning.</p><p>Additionally, since the agent within the chat cannot keep waiting for an artifact to be generated, once the generation tool is triggered, a <strong>new flow</strong> is launched <strong>asynchronously</strong> within the artifact domain (in this case, a quiz), as shown below.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Oh6WAUP-omecZ1GfCt5cQw.png" /></figure><p>The following diagram provides a high-level view of the overall structure — <br> showing how the system responds dynamically to user requests and how the <strong>artifact domain operates independently</strong> once triggered.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*r0nm8PsH-NxTBVS2CVI5OQ.png" /></figure><p>The <strong>Quiz Agent</strong> is the Artifact Agent that operates within the domain described above.</p><p>While the <strong>routing and request handling</strong> for creation, modification, and retrieval are managed by the <strong>Agents Swarm</strong> and the <strong>Content Generation Agent</strong>, the actual logic is executed within the <strong>Artifact Agent</strong> itself. (Details about the Planner and related components are omitted here.)</p><p>For example, as shown in the image below:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*qgNL5_T82ITLSlvEZ1WYVg.png" /></figure><p>When a user says “Create a quiz for me,” the <strong>Agents Swarm</strong> detects the quiz generation intent and delegates the task to the <strong>Content Generation Agent</strong>. That agent then sends a creation request to the <strong>Quiz Backend</strong>, and the resulting <strong>Artifact</strong> is rendered within the chat interface.</p><p>Each Artifact follows domain-specific logic — such as creation, modification, or progress tracking — and in this case, the <strong>Quiz Artifact</strong> is shown loading and then successfully completing its generation.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3lsvpdgRPllQBbyg1G9UtA.png" /></figure><p>This <strong>Artifact</strong> comes with its own dedicated view, allowing users to interact with it independently — whether through direct interaction within the viewer or by creating or modifying new artifacts through chat, as shown above.</p><p>So far, we’ve covered the two core agents. Now, let’s talk about how we designed the system to <strong>efficiently manage and operate these agents</strong>.</p><h4>Separation of Agent and Chat Logic</h4><p>In a system that handles complex learning flows and diverse user interactions, it’s crucial to clearly separate the responsibilities between the <strong>Agent logic</strong> and the <strong>chat interface flow</strong>.</p><p>Initially, our imagined structure had the agent handling the entire process: receiving the user’s message, generating a response, converting it into blocks, and rendering it directly in the chat window.</p><p>However, this tightly coupled structure led to several problems:</p><ul><li>The agent became <strong>too heavy</strong>, increasing complexity and making testing more difficult</li><li>The chat <strong>UI/UX logic was entangled</strong> with agent logic, making reuse and scalability harder</li><li>Any change in chat flow or UI had <strong>unintended side effects</strong> on the agent’s behavior</li></ul><p><strong>👷 Strategy for Architectural Improvement</strong></p><p>To solve this, we redesigned the structure to <strong>decouple</strong> the agent from the chat system and clearly define their individual responsibilities:</p><ul><li>The <strong>Agent</strong> focuses solely on its core role: <em>Understanding user intent → Selecting and using tools → Generating a response</em></li><li>Everything else — such as checking user quota, transforming responses into UI components, managing conversation flow, and rendering — is handled by a separate <strong>backend chat orchestration layer</strong></li></ul><p>Here’s a visual representation we used in our design process to help make this separation easier to understand:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DAUDRYUGMBuzZ2jABCfEpQ.png" /></figure><p>The User label represents the chat interface, the Backend (DB) label refers to the flow handled and stored by the Chat Backend server, and the Agent label corresponds to the Agents Swarm.</p><p>Through this architectural separation, we gained several key benefits:</p><ul><li>✅ <strong>Modularity</strong>: Agents can now be developed independently and reused across different parts of the system. For example, the <strong>Generator Agent</strong> can be released as a standalone product or offered as an API, without being tightly coupled to QANDA’s user interface.</li><li>✅ <strong>Scalability</strong>: The chat interface can now be flexibly adapted to support new flows and UI designs.</li><li>✅ <strong>Stability</strong>: Critical functions tied to system reliability — such as quota management and message flow control — have been moved outside the agent, improving overall robustness.</li><li>✅ <strong>Maintainability</strong>: By ensuring each layer manages only its own responsibilities, we’ve minimized the blast radius of changes and made the system easier to maintain.</li></ul><p>This structure has ultimately laid the foundation for QANDA to expand across a broader range of platforms and channels in the future.</p><h4>Future Directions</h4><p>But there’s still so much more to talk about.</p><p>In our upcoming blog posts, we’ll be diving deeper into topics like:</p><p><strong>🔍 Evaluation</strong></p><p>While the backend engineers were busy designing and implementing the system architecture, our AI engineers focused on future-oriented research — such as Agent Memory and Document Parsers — while also <strong>significantly improving our evaluation framework</strong>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*n8U-UGy-hAVO8F34OMcWbw.png" /></figure><p>In Gen AI systems, <strong>defining and measuring output quality</strong> remains a critical challenge. To address this, QANDA has developed an in-house evaluation dataset that we’ve been continuously refining.</p><p>It includes:</p><ul><li>Evaluation scenarios with diverse input/output cases</li><li>Assessments ranging from individual Agent/Tool units to end-to-end evaluations across complete learning flows</li><li>Other elements that influence chat performance, such as LaTeX rendering, next-response suggestions, and more</li></ul><p>In an upcoming post, we’ll share how this evaluation framework helps accelerate Agent development and refine quality standards in meaningful ways.</p><p><strong>📈 Monitoring: Latency &amp; Cost Optimization</strong></p><p>As mentioned earlier, one of the key challenges in Gen AI systems is striking the right balance between rich, accurate responses and fast, cost-effective execution.</p><p>We continuously monitor several key metrics:</p><p><strong>Latency</strong></p><ul><li>TTFT (Time to First Token): Time from user input to the appearance of the first token</li><li>TPOT (Time per Output Token): Average time between generated tokens</li><li>Total Latency: Time to generate the complete response</li></ul><p><strong>Cost</strong></p><ul><li>Total cost of LLM calls per response</li><li>Cost breakdown per Agent / per Tool</li><li>Cached prompt ratio</li></ul><p>In a future post, we’ll share the optimization strategies we’ve applied based on these metrics, the improvements we’ve achieved, and the guardrail systems we plan to introduce moving forward.</p><p><strong>🧠 Managing Agent Memory</strong></p><p>To become a true learning partner, an Agent must go beyond simple context tracking — it must offer <strong>personalized experiences</strong> by deeply understanding the user.</p><p>For instance, knowing what subject the student is currently studying, which national curriculum they follow, their learning habits, or even personal interests — <strong>all help generate more relevant learning strategies</strong>.<br> This also enhances <strong>CRM capabilities</strong> such as push notifications, reminders, content recommendations, and marketing messages.</p><p>Agent personalization is not a minor feature — it’s a <strong>core strategy</strong> for increasing product engagement and long-term retention.</p><p>Currently, QANDA’s backend team is actively collaborating with AI engineers to explore multiple approaches for building a memory system that retains long-term user context.<br> We’re exploring technical areas such as:</p><ul><li>Storing and retrieving user state with <strong>vector databases</strong></li><li>Designing <strong>short-term and long-term memory entities</strong></li><li>Experiments with <strong>prompt injection</strong> for personalized context</li><li><strong>Encoding strategies</strong></li></ul><p>This goes far beyond just “storing data.” It’s about <strong>observing behavior</strong>, accumulating meaningful context, and delivering better conversations and smarter recommendations.</p><p>We’ll provide a deep dive into the design, structure, and impact of this memory system in a future article.</p><p><strong>🧪 Prompt Management and A/B Testing</strong></p><p>QANDA’s Agent architecture uses <strong>hundreds of prompts</strong>, all of which are systematically managed for use in both <strong>A/B testing</strong> and <strong>evaluation strategies</strong>.</p><p>We believe future AI products will shift more toward <strong>agent-centric designs</strong>, and testing these natural-language-driven systems will look very different from traditional QA and UI testing.</p><p>That’s why we’re building a new kind of testing infrastructure — designed for evaluating conversational intelligence.</p><p>In a future post, we’ll explain:</p><ul><li>How we structure, test, and version our prompts</li><li>What kinds of evaluation and behavior data we collect</li><li>How we apply insights to optimize performance and consistency</li></ul><p><strong>🔧 How We Built It with LangGraph</strong></p><p>Some of you may have already noticed this from the diagrams above:<br> QANDA’s multi-agent architecture — including intent branching, Tool calling logic, Plan-and-Execute strategy, and Artifact-based content generation — has all been implemented using <strong>LangGraph</strong>.</p><p>We’ll share detailed implementation stories, key architectural decisions, and lessons learned from working with LangGraph in an upcoming technical deep dive.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Rn_LRvoM795ISfr8hsiBNQ.png" /></figure><p>In fact, as shown in the image above, even <strong>evaluation</strong> is fully <strong>graph-based and visualized</strong>, allowing us to inspect and debug behaviors in an intuitive way.</p><p><strong>LangGraph</strong> has proven to be a powerful framework for building the kind of architecture we envisioned — handling complex concerns such as inter-agent messaging, state-driven flow control, conditional branching, and session persistence, all with ease and flexibility.</p><p>At QANDA, we’ve been using <strong>LangChain</strong> since its early days, and through numerous use cases, we’ve developed a deep understanding of various implementation patterns — many of which we applied to our current Agent system.</p><p>Through our experience with <strong>LangChain</strong> and <strong>LangGraph</strong>, we’ve gained the following key insights:</p><ul><li>How to define and execute <strong>collaborative workflows</strong> between agents</li><li>How to manage <strong>non-deterministic responses</strong> and <strong>state transitions</strong> in a robust way</li><li>Why we chose LangGraph over LangChain, and what challenges we encountered in taking it to <strong>production</strong></li></ul><p>We plan to share these implementation stories in a future post and we believe they’ll be especially valuable for anyone interested in <strong>LangChain</strong>, <strong>LangGraph</strong>, or building <strong>graph-based reasoning systems</strong> in real-world environments.</p><p>In our next blog post, we’ll dive deeper into the topics above — focusing on the practical challenges and hard-earned insights needed to bring a Gen AI system to a <strong>production-ready state</strong>.</p><p>Thanks for reading, and we hope you’ll stay tuned! 🙌🏻</p><p>Join our team and shape the future of education with us! Check out our careers page <a href="https://team.mathpresso.com/?utm_source=medium&amp;utm_medium=organic&amp;utm_campaign=blog_contents">here</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=73f66934f006" width="1" height="1" alt=""><hr><p><a href="https://blog.mathpresso.com/how-was-the-new-qanda-agent-built-73f66934f006">How Was the New QANDA Agent Built?</a> was originally published in <a href="https://blog.mathpresso.com">Team QANDA</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[새로운 콴다 Agent는 어떻게 만들어졌을까?]]></title>
            <link>https://blog.mathpresso.com/%EC%83%88%EB%A1%9C%EC%9A%B4-%EC%BD%B4%EB%8B%A4-agent%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%A7%8C%EB%93%A4%EC%96%B4%EC%A1%8C%EC%9D%84%EA%B9%8C-0788a7b37b6e?source=rss----35e40785d286---4</link>
            <guid isPermaLink="false">https://medium.com/p/0788a7b37b6e</guid>
            <category><![CDATA[febe]]></category>
            <dc:creator><![CDATA[Dave Kwon]]></dc:creator>
            <pubDate>Tue, 05 Aug 2025 09:23:17 GMT</pubDate>
            <atom:updated>2025-08-07T07:48:29.960Z</atom:updated>
            <content:encoded><![CDATA[<h4>콴다는 왜 달라졌을까? Backend 팀의 Agent 설계 이야기</h4><p>안녕하세요, 콴다에서 Backend 엔지니어로 일하고 있는 Dave입니다.<br>최근 콴다의 달라진 모습을 보시고 놀라신 분들도 계실 것 같습니다. Gen AI 시대에 발맞춰 저희는 단순한 기능 개선을 넘어서, 앱의 메인 화면부터 사용자 흐름 전반에 이르기까지 전면적인 재설계를 진행했습니다.</p><p>이번 블로그에서는</p><ul><li>Gen AI 시대에 콴다가 제시하는 새로운 AI 제품의 방향성</li><li>그 비전을 실현하기 위해 백엔드 팀이 어떤 기술적 접근을 했는지</li></ul><p>그 과정을 하나씩 풀어보려 합니다. AI 기반 서비스의 설계와 구조에 관심 있는 분들께 실질적인 참고가 되기를 바랍니다.</p><h3>콴다는 왜, 어떻게 바뀌었을까?</h3><p>기존 콴다 앱은 학생이 모르는 문제를 사진으로 찍으면 AI가 풀이를 제공하는 기능이 대표적이었습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/720/1*zT_MFgSl0AG6EpI2D_Iv8Q.png" /></figure><p>하지만 이 기능은 특정 유즈케이스, 즉 “문제를 모를 때 풀이를 제공받는” 흐름에 집중되어 있었기 때문에, 다양한 학습 목적(개념 설명, 유사 문제 추천, 퀴즈 생성 등)을 다루기에는 제약이 있었습니다.</p><h4>🗺 새롭게 정의한 방향</h4><p>그래서 저희는 다음과 같은 목표를 새로 세웠습니다:</p><blockquote>“사용자의 학습 목적을 실시간으로 이해하고, 그에 맞춰 LLM을 활용해 최적의 UI/UX를 제공하자.”</blockquote><p>이를 통해 새로워진 콴다는 단순한 문제 풀이 도우미가 아닌, 다양한 학습 요구에 대응할 수 있는<strong> 능동적인 AI 어시스턴트</strong>로 재탄생했습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*iOVeHrMtaaZ3zSluGx3MVA.png" /></figure><h3>완전히 새로운 개발 패러다임</h3><p>새로 만든 시스템의 본질은 흔히 말하는 <strong>“에이전트(Agent)”</strong>였습니다.<br>단순히 미리 짜둔 로직대로 반응하는 것이 아니라, 사용자의 다양한 요청을 이해하고 그에 맞는 도구(Tool)를 선택해 문제를 해결할 수 있는 구조가 필요했습니다.</p><p>초기에는 일반적인 제품 개발처럼 유저의 요구사항을 나열하고, 그에 맞는 기능을 정의해 UI로 구현했습니다. 예를 들어:</p><ul><li>“이 문제를 어떻게 푸는지 모르겠어요.” → Step-by-step 풀이 제공</li><li>“제가 직접 푼 답인데 맞았는지 확인해주세요.” → 자필 풀이 첨삭</li><li>“개념이 이해되지 않아요.” → 개념 설명 및 예시 제공</li><li>“비슷한 문제를 더 풀고 싶어요.” → 유사 문제 검색</li><li>“이제 좀 이해된 것 같아요.” → 퀴즈 생성으로 이해도 점검</li></ul><p>이런 요구사항들을 기반으로 기존 방식대로라면 각각의 기능을 담당하는 API를 설계하고, 화면에 맞는 컴포넌트를 구성했을 것입니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RBqfEsqujO23VXQ-T3OEgQ.png" /></figure><p>하지만 저희는 학생과의 상호작용을 <strong>단일한 채팅 인터페이스 위에서 통합된 Agent가 주도하는 구조</strong>를 선택했습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*aSdyqblujFrBq44JtPhHcg.png" /></figure><p>즉, 하나의 대화 흐름 안에서 다양한 요구에 대응할 수 있는 구조를 목표로 했으며, 이를 실현하기 위해 개발 측면에서 핵심적으로 고민해야 했던 세 가지는 다음과 같습니다:</p><p>✅ 1. <strong>채팅 인터페이스 컴포넌트 정의</strong><br>→ 유저의 요청에 따라 다양한 Block을 조합해 표현해야 함</p><p>✅ 2. <strong>Agent의 의도 분석<br></strong>→ 자연어로부터 학습 목적을 파악해야 함</p><p>✅ 3. <strong>Agent가 사용할 Tool 체계 설계</strong><br>→ 도구를 조합해 복합 작업을 해결할 수 있어야 함</p><p>이렇게 해서, 최종적으로 아래와 같은 세 가지 개념으로 모든 시스템을 추상화할 수 있었습니다:</p><blockquote><strong><em>Agent (의도 이해) → Tool (문제 해결) → Block (시각적 응답)</em></strong></blockquote><h3>학생의 의도 분석하기</h3><p>앞서 언급했듯, 콴다가 다양한 학습 유즈케이스를 유연하게 지원하기 위해서는 무엇보다 <strong>학생의 의도를 정확히 파악하는 것</strong>이 중요합니다.</p><p>예를 들어, 같은 문제 사진을 올리더라도</p><ul><li>단순히 풀이만 원하는 경우</li><li>자신의 풀이를 첨삭받고 싶은 경우</li><li>개념 설명을 듣고 싶은 경우</li><li>유사한 문제를 더 풀고 싶은 경우</li><li>해당 내용을 기반으로 퀴즈를 만들고 싶은 경우</li></ul><p>등 다양한 요구가 존재합니다. 하지만 현실적으로 현재의 LLM이 이 모든 의도를 <strong>one-shot으로 완벽하게 분류</strong>하기는 쉽지 않습니다.</p><p>또한, 앞으로도 계속해서 새로운 학습 요구와 유즈케이스가 등장할 것이기 때문에, <strong>처음부터 확장 가능하고 유연한 구조</strong>를 만드는 것이 중요했습니다.</p><h4>🤖 의도 분석과 실행을 위한 다중 에이전트 전략</h4><p>그래서 저희는 먼저, 우리는 유저의 니즈를 크게 두 가지 흐름으로 분리했습니다:</p><ul><li><strong>문제 풀이 중심: </strong>첨삭, 해설, 개념 설명 등 문제 해결을 돕는 플로우</li><li><strong>학습자료 생성 중심: </strong>퀴즈, 플래시카드, 정리노트 등 콘텐츠 생성 플로우</li></ul><p>이 분리를 기반으로 각 흐름을 담당하는 Agent를 독립적으로 만들기로 했고, 그 결과 다음과 같은 구조가 나왔습니다:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sEQlzh5Z8deCIe6bTnnRcQ.png" /></figure><h4>🕸️ Multi-Agent Swarm Architecture</h4><p>이 구조는 흔히 <strong>Swarm Architecture</strong>라 불리는 접근 방식과 유사합니다(<a href="https://langchain-ai.github.io/langgraph/agents/multi-agent/#swarm">참고</a>). 각 Agent는 자신의 <strong>전문 관심사(도메인)</strong>에 집중하며, 담당 범위를 벗어나는 요청은 다른 Agent에게 <strong>hand-off</strong> 하는 구조입니다.</p><p>첫 요청은 determine_active_agent에서 문제 풀이 Agent(Answer Question Agent)와 자료 생성 Agent(Generate Learning Material Agent)를 적절히 선택해 책임을 이관하고, 이후 요청부터는 현재 책임인 Agent가 맡다가, 컨택스트가 바뀌면 다른 Agent에게 hand-off 하는 모습이죠.</p><p>(참고로, info_agent는 콴다의 정책 등을 알려주는 Agent, vanila_agent는 쿼타를 다 사용한 유저들을 위한 무료 Agent입니다.)</p><p>이 방식의 장점은 다음과 같습니다:</p><ul><li>✅ <strong>컨텍스트를 간결하게 유지할 수 있음</strong><br> → 각 Agent는 자신의 역할만 알면 되므로, 프롬프트나 히스토리를 최소화할 수 있습니다.</li><li>✅ <strong>평가와 테스트가 유즈케이스별로 명확해짐</strong><br> → 유즈케이스별로 평가 지표와 테스트 시나리오를 분리해 설계할 수 있습니다.</li><li>✅ <strong>개발자 입장에서 관심사가 분리되어 목표 중심 개발이 가능</strong><br> → 각 Agent는 명확한 목적과 책임을 가지므로 개발자가 목표 중심으로 작업 가능</li><li>✅<strong> Agent의 확장성과 재사용성이 뛰어남</strong><br> → 새로운 유즈케이스가 등장해도, Agent를 독립적으로 추가하거나 조합 가능<br> → 동일한 Agent를 다른 시나리오에서 재사용할 수 있음</li></ul><p>하지만 모든 개발에 trade-off 가 있듯, Swarm 구조도 목표를 해결하는데까지 걸리는 시간과 비용이 증가한다는 단점이 있지만, 그 해결 과정은 다음에 별도로 다루겠습니다.</p><p>다음에는 각각의 agent가 가진 특징을 얘기해볼게요.</p><h3>문제 풀이 Agent: 계획하고 수행하기</h3><p>가장 먼저 개발에 착수한 것은 <strong>문제 풀이 Agent</strong>였습니다. 이 Agent는 다음과 같은 다양한 학습 요구를 해결해야 했습니다:</p><ul><li>모르는 문제에 대한<strong> step-by-step 풀이 제공</strong></li><li><strong>유사 문제 추천</strong>: 콴다 DB에서 문제 유형을 분석하여 연관 문제 제공</li><li><strong>자필 풀이 첨삭</strong>: 학생이 직접 푼 풀이를 검토하고 피드백 제공</li><li><strong>그래프 생성</strong>: 필요 시 함수나 기하 개념을 시각화</li><li><strong>다수 문제 처리</strong>: 한 이미지에 여러 문제가 있을 경우 순차적으로 분리 및 풀이</li><li><strong>용어 설명</strong>: 풀이 중 등장하는 개념에 대한 보충 설명</li></ul><p>이외에도 시스템적인 제약과 목표가 있었습니다:</p><ol><li><strong>최소 정답률(n%)</strong> 이상을 유지할 것</li><li><strong>DB 기반 문제 활용 비율을 높여 비용 최적화</strong>할 것</li></ol><h4>🧠 초기 접근: ReAct 기반 구조</h4><p>처음에는 각 기능을 <strong>Tool로 모듈화</strong>하고, Agent가 질문의 맥락에 따라 적절한 Tool을 호출해 문제를 해결하는 구조를 상상했습니다. 이 방식은 일종의 <strong>ReAct 전략</strong>에 가까우며(<a href="https://www.ibm.com/think/topics/react-agent">참고</a>), 아래와 같은 흐름을 따릅니다:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*74hgC7rYog2LZQ5aV4z-Ng.png" /><figcaption>Simple ReAct Agent</figcaption></figure><p>그러나 실제 Evaluation을 진행해보니 다음과 같은 한계점이 드러났습니다:</p><p>전체적인 응답의 일관성이 부족했고 특히 “<strong>다수 문제 처리”</strong> 시 흐름을 놓치거나 <strong>문제 풀이 순서가 꼬이는 현상</strong>이 자주 발생했습니다. 이를테면, 아래와 같은 이미지와 함께 두 문제 씩 풀어달라는 요청을 넣었을 때,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kHqVhdGdHXgWCRnPTZR-Sg.png" /></figure><p>1번과 2번을 풀고, 다음 세션에는 3번과 4번을 마저풀기를 기대하는데, 다시 1번과 2번을 풀거나 3번을 건너띄어 4번을 푸는 등의 문제가 있었습니다.</p><p>또한, 콴다 DB를 먼저 살펴보고 정확히 일치하는 문제가 있으면 그걸 줘야하는데, Agent가 이러한 전체 맥락을 고려하지 않고 눈앞의 문제만 빠르게 처리하려는 <strong>‘greedy’한 응답</strong>을 보이는 경향이 강했습니다.</p><p>물론, 문제 풀이 테스크만 시켰다면 훨씬 잘 했겠지만, 문제 풀이 Agent는 다양한 복합 요구를 처리해야 했기 때문에 다른 방식이 필요했습니다.</p><h4>👉 전략 전환: Plan-and-Execute 구조 도입</h4><p>이에 따라, 단순한 도구 호출 중심의 ReAct 방식에서 벗어나, “<strong>선 계획(Plan) → 후 실행(Execute)”</strong>의 구조로 아키텍처를 변경했습니다. (<a href="https://langchain-ai.github.io/langgraph/tutorials/plan-and-execute/plan-and-execute/">참고</a>)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LxJ9XXLJHoWekMd7QfiBsQ.png" /></figure><p>Plan 단계에서는 전체 요청의 구조를 파악하고, 어떤 작업이 필요한지 미리 순서를 정리한 뒤, Execute 단계에서는 그 계획에 따라 하나씩 Tool을 호출하고, 상태를 업데이트하며 진행합니다. Replan 단계는 Optional인데, 테스크를 수행한 뒤 결과를 가지고 다시 Plan에 반영하는 단계입니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VGQhY6Ql3W_9ypfqGzNnnw.png" /></figure><p>그래프가 조금 복잡하기는 하지만, 위는 실제로 live 된 문제 풀이 Agent의 구조입니다. 위에 설명드린 것과 같이, Plan / Execute / Replan 과정을 거치며 문제를 풀어가는 계획을 완수합니다.</p><p>이 방식은 <strong>다단계 문제</strong>, <strong>순차적 요구 처리</strong>, <strong>복합 응답 생성</strong>에 훨씬 안정적인 성능을 보였습니다. Agent가 보다 구조적으로 사고하고, 일관성 있는 응답을 제공할 수 있었기 때문입니다. 또한, 위에서 문제가 되었던 greedy함도 Replan단계를 도입하여 같은 문제 여부를 판단할 수 있게 되었기 때문에 쉽게 해결이 되었습니다.</p><h4>🦾 콴다의 문제 풀이 보조</h4><p>이렇게 만들어진 문제 풀이 Agent가 문제 데이터를 가지고 학습을 보조하는 실제 프로덕트의 모습을 살펴보도록 하겠습니다:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*TBQIMgcIz3SPIHXPFl5yFg.png" /></figure><p>위 사진은 등비수열 문제를 물어봤을 때, 유저의 풀이를 기반으로 첨삭풀이를 제공한다는 Plan을 세운 후, 첨삭 Tool을 사용하여 Execute된 결과물을 보여주는 화면입니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vrmxjlwFScqQKfCDdt9iaA.png" /></figure><p>위와 같은 플로우차트를 보시면 이해가 좀 더 쉬울 수는 있는데, 유저의 요청을 보고 Planner가 어떤 Tool을 사용할지 정해서 Executor에게 전달하는 모습을 볼 수 있습니다.</p><p>반면,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*x2xhw163ac7rtRUfmz6Lbw.png" /></figure><p>유저가 위처럼 같은 이미지지만 풀이 검색을 같이 원한다면,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UMIIFAaX3VDXGnfbFHaEyQ.png" /></figure><p>위 플로우차트처럼 Planner가 콴다 DB를 검색하는 검색 Tool을 사용하라고 계획을 짜주고, 이에 따라 Exectuor가 실행해 문제를 찾아서 줄 수 있겠죠.</p><p>실제로는 다양한 usecase를 많이 대응하고 있어서 Planner의 역할은 훨씬 중요하고 이에 따라 콴다에서는 많은 가이드라인 프롬프트를 적용하고 있어요.</p><p>이처럼 문제 풀이 Agent는 콴다 시스템 내에서도 가장 먼저 설계되고, 가장 많은 시나리오를 다루는 Agent입니다. 때문에 제품 개발에 더불어 <strong>추론, 도구 활용, 사용자 요청 처리 등 Agent 설계의 핵심 개념을 테스트하는 실험장</strong> 역할을 했습니다.</p><p>콴다 Backend 팀은 문제 풀이 Agent를 만들며, ReAct와 Plan-and-Execute 방식을 비롯한 다양한 구조들을 Evaluate해보고 어떤 구조는 어느 테스크에 적합할지 노하우를 쌓아갔습니다. 그리고 Agent 플로우에서 오류가 나면 어떻게 처리할 지, 모델에 주는 메시지와 응답으로 주는 메시지는 어떻게 분리할 지와 같은 여러가지 현실적인 문제들에 대한 해결도 가능해졌습니다.</p><p>이렇게 얻은 인사이트는, 이후에 개발한 <strong>Generator Agent</strong>, 즉 <strong>콘텐츠 생성 흐름</strong>을 설계할 때도 큰 기반이 되어주었습니다. 다음으론, 단순한 문제 풀이를 넘어, <strong>학생의 학습을 더 능동적으로 도와주는 콘텐츠 생성 Agent</strong>는 어떻게 설계되었는지를 소개합니다.</p><h3>자료 생성 Agent: 컨텐츠를 생성하기</h3><p>문제 풀이 Agent가 ‘정답을 찾는 과정’에 집중되어 있다면, <strong>자료 생성 Agent는 학습을 설계하는 과정</strong>, 즉 학습자가 <strong>직접 상호작용하며 학습 효과를 끌어올리는 흐름</strong>을 담당합니다.</p><p>예를 들어 퀴즈나 플래시카드를 생성하는 플로우는 단순한 응답 생성으로 끝나지 않습니다.</p><ul><li>콘텐츠 자체를 생성하는 데 시간이 더 걸릴 뿐만 아니라,</li><li>이후 <strong>퀴즈 풀기, 채점, 재생성, 편집</strong> 등 복수의 상호작용이 발생하며</li><li>기존 챗 기반 화면을 넘는 <strong>독자적인 UI와 흐름</strong>이 필요합니다.</li></ul><p>이처럼 새로운 유형의 상호작용을 고려해야 할 때, 다양한 LLM 기반 제품군에서는 이를 <strong>Canvas</strong> 또는 <strong>Artifact</strong>라고 부르며, 기존 챗 구조와 구분되는 별도 인터페이스로 정의합니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*3eQYssKBELjtj_KI" /></figure><p>저희도 콴다 내에서 이러한 콘텐츠 중심의 경험을 정확히 정의하고 시스템적으로 분리하기 위해 <strong>”Artifact”</strong>라는 용어를 도입하게 되었습니다.</p><h4>🎛️ Artifact란 무엇인가?</h4><p>Artifact는 단순히 하나의 결과 블록이 아닌, <strong>별도의 창(뷰어)에서 상호작용이 일어나며, 그 결과가 시스템 상태와 AI 응답에 영향을 미치는 독립적인 학습 경험 단위</strong>입니다.</p><p>Artifact의 특징은 다음과 같습니다:</p><ul><li>사용자는 <strong>별도 뷰어 안에서 상호작용</strong>하며 정보를 직접 변경할 수 있습니다.</li><li><strong>변경된 정보는 이후 AI 응답의 맥락에 포함</strong>되며, 대화 흐름에 영향을 줍니다. ex) 퀴즈를 아직 풀지 않은 상태 vs 퀴즈를 풀고 채점까지 완료한 상태에서는 LLM이 제시할 수 있는 피드백과 응답이 완전히 달라집니다.</li><li>이러한 구조를 지원하기 위해, Artifact는 백엔드 관점에서도 일반 답변과는 달리 <strong>데이터 저장, 변경 이벤트 추적, 상태 반영 로직</strong>이 필요하며, 경우에 따라 <strong>별도 서비스로 분리</strong>되기도 합니다.</li></ul><h4>❓언제 Artifact인가?</h4><p>개발 관점에서 Artifact는 보통 다음과 같은 기준으로 판단할 수 있습니다:</p><ul><li><strong>CRUD 중 R(조회) 외의 이벤트가 발생</strong>할 경우, 즉, 사용자의 행위에 따라 시스템 내부 상태가 바뀌는 경우</li><li><strong>별도의 세션 상태나 결과 저장이 필요</strong>한 경우</li><li>일반 챗 메시지 흐름으로 대응이 불가능하거나, <strong>UI가 분리된 작업 단위</strong>가 필요한 경우</li><li>예시: 퀴즈풀기 + 채점 / 앱 필기 열람 + 편집 등</li></ul><p>Artifact는 단순한 결과 뷰가 아니라, <strong>“사용자 경험이 분기되고, 데이터가 변화하며, 시스템 맥락에 영향을 주는 지점”</strong>입니다.</p><p>Generator Agent는 바로 이러한 Artifact 기반 경험을 주도하는 Agent이며, 학생이 <strong>더 능동적으로 학습에 참여하도록 설계된 도우미</strong>라고 할 수 있습니다.</p><h4>🧮 콴다의 Artifact가 만들어지는 구조</h4><p>이렇게 정의를 하고, 만들어낸 Generator Agent와 그 상호작용 방식의 구조는 다음과 같습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CC20lk39o_27grdywwsi9w.png" /></figure><p>우선 Agent의 구조에 대한 얘기를 하자면, 여기서도 물론 Plan-and-Execute를 사용하였는데 아티팩트에 대한 수정, 생성 등 다양한 인터렉션을 지원해야했기 때문입니다. 조금 다른 점은 조건부 실행등 복잡한 복합로직이 없기 때문에 Replan까지는 사용안했다는 점입니다.</p><p>또한 채팅 내의 Agent가 아티팩트 생성을 계속 기다릴 수 없기에, 생성 Tool이 작동하면, 비동기적으로 아래와 같이 아티팩트(여기선 퀴즈) 도메인의 새로운 플로우를 실행합니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kI5y-rizmusjq_AE5By8xQ.png" /></figure><p>이게 전체적으로 유저의 요청에 따라 어떻게 변하는지, 아티팩트 도메인은 어떻게 분리되어 움직이는지에 대한 큰 시야에서의 구조는 아래와 같습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ImVS7BDItyIJ2GEOTO0nrw.png" /></figure><p>Quiz Agent가 바로 위에서 설명한 도메인에 따른 Artifact Agent입니다. 생성 / 수정 / 읽기에 대한 라우팅과 요청은 Agents Swarm과 자료 생성 Agent에서 하되, 실제 돌아가는 로직은 Artifact Agent에서 하는 거에요. (Planner 등의 설명은 여기서는 생략했습니다.)</p><p>이를 테면 아래 사진처럼,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*w8hwztcLcKWmYbDxF5-2Fg.png" /></figure><p>사용자가 퀴즈 만들어줘라고 하면 Agents Swarm은 퀴즈 생성의도를 알아채서, 자료 생성 Agent에게 일을 맡깁니다. Quiz Backend에 생성 요청을 보내고 Artifact를 채팅에 띄웁니다.</p><p>각각의 Artifact는 도메인에 맞는 로직이 있고(생성, 수정, 진행사항 확인 등) 퀴즈 Artifact는 이에 따라 로딩을 하다가 생성완료가 된 모습이에요.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Gzoba0mvQz3dl-zntBYCGA.png" /></figure><p>이 Artifact는 자신만의 뷰가 있어서 위 처럼 따로 인터렉션을 할 수도, 따로 채팅에서 새로운 Artifact 생성, 수정도 할 수 있습니다.</p><p>이렇게 두 가지 Agent에 대한 얘기를 드렸는데요, 다음은 이러한 Agent들을 어떻게 잘 운영 관리하도록 만들었는지에 대한 이야기를 풀어보겠습니다.</p><h3>Agent와 채팅의 분리</h3><p>복잡한 학습 흐름과 다양한 유저 상호작용을 다루는 시스템에서는, <strong>Agent의 역할</strong>과 <strong>채팅 인터페이스의 흐름 관리</strong>를 명확히 분리하는 것이 중요합니다.</p><p>초기 상상한 구조는 Agent가 사용자 메시지를 받아, 곧바로 응답을 생성하고, 해당 메시지를 블록으로 변환해 채팅창에 표시하는 전체 과정을 책임지는 구조였습니다. 하지만 이 구조에서는 Agent가 지나치게 많은 역할을 떠안게 되고, 그로 인해 다음과 같은 문제가 발생했습니다:</p><ul><li>Agent가 너무 무거워져 <strong>복잡도와 테스트 난이도 증가</strong></li><li>채팅 UI/UX와 Agent 로직이 얽혀 <strong>재사용이 어려움</strong></li><li>채팅 흐름이나 UI 변경이 있을 때마다 Agent에 <strong>불필요한 영향 전파</strong></li></ul><h4>👷 구조 개선 전략</h4><p>그래서 우리는 <strong>Agent와 채팅 흐름 사이의 결합도를 낮추고</strong>, 각자의 책임을 분리하는 방향으로 구조를 개선했습니다.</p><ul><li><strong>Agent는 본연의 역할인 ‘의도 이해 + Tool 활용 + 응답 생성’에만 집중</strong></li><li>사용자 메시지를 받아 쿼타 확인 / 채팅 디자인 컴포넌트 변환 / 대화 흐름 구성 및 렌더링 등을 처리하는 기능은 <strong>별도의 Backend 채팅 관리 레이어</strong>로 분리</li></ul><p>이해하기 쉽게 설계에 사용했던 사진을 첨부합니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*oUwbyxUB_6Uof75-o3l0MA.png" /></figure><p>User 라벨이 채팅창을, Backend(DB) 라벨이 Chat Backend 서버에서 저장되는 흐름의 구조를, Agent 라벨이 Agents Swarm을 의미한다고 보시면 될 거 같습니다.</p><p>이 구조적 분리를 통해 우리는 다음과 같은 이점을 얻었습니다:</p><ul><li>✅ <strong>모듈화</strong>: Agent는 상황에 맞게 독립적으로 개발되고, 다른 에이전트에도 재사용 가능하게 되었습니다. 예를 들면, Generator Agent는 콴다 유저와의 디펜던시 없이 새로운 제품으로 출시되거나 api화 해서 제공할 수 있습니다.</li><li>✅ <strong>확장성</strong>: 채팅 인터페이스는 새로운 흐름이나 UI에 맞게 유연하게 수정 가능해졌습니다.</li><li>✅ <strong>안정성</strong>: 유저 쿼터 관리나 메시지 흐름 제어 같은 시스템 안정성과 직결되는 기능이 Agent 외부로 이동하였습니다.</li><li>✅ <strong>유지보수 용이성</strong>: 각 레이어가 자신의 책임만 관리함으로써 변경 시 영향 범위를 최소화하였습니다.</li></ul><p>이러한 구조는 결과적으로 콴다가 앞으로 더 다양한 채널로 확장할 수 있는 기반이 되었습니다.</p><h3>그 외, 더 나누고 싶은 이야기들</h3><p>이번 글에서는 콴다 백엔드 팀이 어떤 구조적 고민을 바탕으로 AI 기반 학습 어시스턴트를 설계하고 구축해왔는지 중심으로 소개해 드렸습니다.</p><p>하지만 아직 풀지 않은 이야기들이 많이 남아 있습니다.</p><p>다음 블로그에서는 아래와 같은 주제들을 보다 <strong>풍부하고 깊이 있게 다룰 예정</strong>이니 기대해주세요.</p><h4>🔍 평가하기 (Evaluation)</h4><p>콴다의 백앤드 엔지니어들이 시스템 아키텍처를 설계하고 구현하는 동안,<br> AI 엔지니어들은 Agent Memory, Document Parser 등 미래 기능과 관련된 리서치와 함께 <strong>Evaluation 체계 고도화</strong>에 집중해왔습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/696/1*0U7dkdSNz6UTLdpBQVQP2w.png" /></figure><p>Gen AI 시스템에서 <strong>출력 품질을 어떻게 정의하고 측정할 것인가</strong>는 여전히 매우 중요한 과제이며, 이를 위해 콴다 내부에는 이미 축적된 자체 평가용 데이터셋이 존재합니다.</p><ul><li>다양한 유형의 입력 / 출력 케이스를 포함한 평가 시나리오</li><li>Agent / Tool 단위 평가부터, 전체 학습 흐름에 대한 종단 평가까지</li><li>그 외 Latex / 다음 응답 제안 등 채팅 환경에 영향을 미치는 각종 요소</li></ul><p>이러한 평가 프레임워크가 <strong>Agent 개발 속도를 높이고, 품질 기준을 정교화하는 데 어떻게 기여하고 있는지</strong>를 상세히 소개드릴 예정입니다.</p><h4>📈 모니터링하기 (Latency &amp; Cost Optimization)</h4><p>앞서 간단히 언급드린 바와 같이, Gen AI 시스템은 <strong>정확하고 풍부한 응답</strong>과 <strong>빠르고 저렴한 응답</strong> 사이에서 균형을 잡는 것이 관건입니다.</p><p>저희는 다음과 같은 주요 지표들을 지속적으로 모니터링하고 있습니다:</p><p><strong>Latency (응답 지연 시간)</strong></p><ul><li>TTFT (Time to first token): 사용자가 메시지를 입력한 시점부터 첫 토큰이 떨어져 보이기 까지 걸린 시간</li><li>TPOT (Time per output token): 토큰과 토큰 사이의 걸린 시간</li><li>Total Latency: 전체 응답이 완성되는데 걸린 시간</li></ul><p><strong>Cost (모델 호출 비용)</strong></p><ul><li>전체 응답에 들어간 LLM 호출 비용</li><li>Agent 별 / Tool 별 호출 비용 추적</li><li>Cached Prompt 비율</li></ul><p>이러한 메트릭들을 기반으로 실제 어떤 최적화 전략을 적용했고, 어떤 개선 효과를 얻었는지, 그리고 앞으로 어떤 guardrail 시스템을 도입할 계획인지도 다음 편에서 함께 나눌 예정입니다.</p><h4>🧠 메모리 관리하기</h4><p>Agent가 진정한 학습 파트너가 되기 위해서는, 단순한 문맥 이해를 넘어서 <strong>사용자를 깊이 이해하는 개인화 기능</strong>이 필수입니다.</p><p>예를 들어, 학생이 <strong>현재 어떤 과목을 학습 중인지</strong>, <strong>어느 국가의 커리큘럼을 따라가고 있는지</strong>, 혹은 <strong>학습 외 관심사나 학습 습관은 어떤지</strong> 등의 정보를 바탕으로 보다 정교하게 <strong>맞춤형 학습 방법</strong>을 추천할 수 있고 푸시 메시지, 리마인더, 콘텐츠 추천, 마케팅 메시지 등 <strong>CRM 전략</strong> 측면에서도 훨씬 효과적인 대응이 가능해집니다.</p><p>이처럼 Agent 개인화는 단순한 기능 개선이 아니라, 제품의 지속성과 몰입도를 높이기 위한 핵심 전략입니다.</p><p>현재 콴다의 Backend 팀은 AI 엔지니어들과 협력하여, <strong>Agent에게 지속적인 사용자 정보와 맥락을 기억시킬 수 있는 메모리 체계</strong>에 대해 다양한 방향으로 리서치 중입니다. 구체적으로는 다음과 같은 기술 요소들을 탐색하고 있습니다:</p><ul><li><strong>Vector DB 기반 </strong>사용자 상태 저장 및 검색</li><li><strong>Short-Long Term Memory 엔티티</strong> 설계</li><li>개인화 정보 삽입을 위한 <strong>Prompt 주입 방식 실험</strong></li><li><strong>encoding전략</strong> 등</li></ul><p>개인화 메모리는 단순히 데이터를 저장하는 것을 넘어서, <strong>어떻게 사용자의 행동을 관찰하고, 축적된 맥락을 기반으로 더 나은 대화와 추천을 만들 것인가</strong>에 대한 고민이 담긴 영역입니다.</p><p>이 시스템이 어떻게 설계되고 있고, 어떤 기준으로 메모리가 구성되며, 실제 응답 품질에 어떤 영향을 미치는지에 대한 구체적인 구현 이야기는 나중에 더 깊이 소개드릴 예정입니다.</p><h4>🧪 Prompt 관리와 A/B Test</h4><p>콴다 Agent에는 수백개의 Prompt가 있으며, 이는 체계적으로 관리되어 A/B Test와 Evaluation 전략에 사용됩니다.</p><p>콴다는 앞으로의 AI 프로덕트는 좀 더 Agent 중심이 될 가능성이 높고, 지금까지의 테스트 전략과는 사뭇 다른 모습의 자연어 기반 응답 서비스에 대한 테스트 과정을 만들어 나갈 거라 예측합니다.</p><p>이에 우리가 어떻게 프롬프트들을 테스트 하고 있고, 어떤 데이터를 수집하며, 어떤 매트릭을 보고 있는지에 대해 앞으로 블로그를 통해 소개해드리겠습니다.</p><h4>🔧 LangGraph 기반 구현 이야기</h4><p>위에 첨부된 구조 사진들 몇몇에서 이미 눈치채신 분들도 있을텐데, 콴다의 멀티 에이전트 아키텍처, 의도 분기, Tool 호출 로직, Plan-and-Execute 전략, Artifact 기반 콘텐츠 생성 등은 모두 <strong>LangGraph </strong>기반으로 실제 구현되었습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/536/1*0bUxC0YIkyOFGj6QDRdZxg.png" /></figure><p>심지어 위 사진처럼 Evaluation 마저도 모두 시각화 가능한 그래프 기반으로 작동합니다.</p><p>LangGraph는 우리가 고민했던 구조적 문제 ― 예를 들어 에이전트 간의 메시지 전달, 상태 기반 흐름 제어, 조건 분기, 세션 유지 등 ― 을 쉽고 빠르게 구성할 수 있는 강력한 프레임워크였습니다.</p><p>또한 콴다는 LangChain을 처음 나왔을 때부터 사용하고 있었는데, 많은 사용사례가 정착되어 다양한 구현방법들을 터득했고, 이번 Agent에도 적용했습니다.</p><p>이렇게 LangChain / LangGraph를 활용하면서 얻게 된 인사이트는 다음과 같습니다:</p><ul><li>에이전트 간 협업을 어떻게 정의하고 실행했는가</li><li>비결정적 응답 처리와 상태 전이에 대해 어떤 방식으로 설계했는가</li><li>LangChain보다 LangGraph를 선택한 이유와, 실제 프로덕션 환경에 올리는 과정에서의 시행착오</li></ul><p>이러한 구현 경험을 다음 번에 소개드린다면 <strong>LangChain, LangGraph, Graph 기반 추론 시스템</strong>에 관심 있는 분들께도 실질적인 인사이트가 될 수 있을 것 같습니다.</p><p>다음 블로그에서는 위 주제들을 중심으로, <strong>Gen AI 시스템을 운영 가능한 수준으로 끌어올리는 데 필요한 실질적인 고민과 노하우</strong>를 더 깊게 다뤄보겠습니다.</p><p>많은 관심 부탁드립니다. 감사합니다! 🙌🏻</p><p>🌏 글로벌 Top 교육 앱 QANDA(콴다)를 함께 만들어 갈 분들을 기다리고 있습니다!</p><p>👉 자세한 사항은 <a href="http://bit.ly/3pVjlmR">채용 사이트</a>를 통해 확인해주세요.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=0788a7b37b6e" width="1" height="1" alt=""><hr><p><a href="https://blog.mathpresso.com/%EC%83%88%EB%A1%9C%EC%9A%B4-%EC%BD%B4%EB%8B%A4-agent%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EB%A7%8C%EB%93%A4%EC%96%B4%EC%A1%8C%EC%9D%84%EA%B9%8C-0788a7b37b6e">새로운 콴다 Agent는 어떻게 만들어졌을까?</a> was originally published in <a href="https://blog.mathpresso.com">Team QANDA</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[지적 호기심이 이끄는 커리어 여정 | 콴다 Legal Division Head]]></title>
            <link>https://blog.mathpresso.com/%EC%A7%80%EC%A0%81-%ED%98%B8%EA%B8%B0%EC%8B%AC%EC%9D%B4-%EC%9D%B4%EB%81%84%EB%8A%94-%EC%BB%A4%EB%A6%AC%EC%96%B4-%EC%97%AC%EC%A0%95-%EC%BD%B4%EB%8B%A4-legal-division-head-65f9d9bf2054?source=rss----35e40785d286---4</link>
            <guid isPermaLink="false">https://medium.com/p/65f9d9bf2054</guid>
            <category><![CDATA[culture]]></category>
            <category><![CDATA[legal]]></category>
            <category><![CDATA[careers]]></category>
            <category><![CDATA[startup]]></category>
            <category><![CDATA[people]]></category>
            <dc:creator><![CDATA[People Team]]></dc:creator>
            <pubDate>Tue, 20 May 2025 02:44:37 GMT</pubDate>
            <atom:updated>2025-05-20T02:44:37.675Z</atom:updated>
            <content:encoded><![CDATA[<p>단순한 법률 검토를 넘어, 빠르게 변화하는 비즈니스 환경 속에서 <strong>실질적인 해결책을 제시하는 법무팀</strong>은 어떤 모습일까요?</p><p>실제 콴다 팀에서 그 역할을 수행하고 있는 구성원, Kilian의 인터뷰 내용을 통해 <strong>법무 전문가로서의 커리어 여정</strong>, <strong>업무에 대한 철학</strong>, 그리고 <strong>콴다에서의 일하는 방식</strong>에 대한 생생한 이야기를 전해드립니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*hII7rejZDtjMRkco" /></figure><p>사람들은 어디까지 자신의 인생을 계획하고 꾸려갈 수 있을까요. LA다저스의 슈퍼스타 오타니는 고등학교 1학년 때 만다라트를 만들어서 실천한 것으로 아직도 회자되고 있다면, 다른 쪽에는 수많은 도전과 경험을 통해 귀납적으로 스스로를 깨달아 나가는 사람들도 있습니다. ‘하루하루는 성실하게, 인생 전체는 되는 대로’ 사는 것이, 어쩌면 불확실성이 점점 커지고, 점점 빠르게 변하고 있는 지금의 시기를 현명하게 나는 방법이 아닐까요.</p><p>국내 최대 로펌의 변호사에서 AI 교육 스타트업의 법무팀장으로 커리어를 전환한 변호사님의 여정은 급변하는 시대에 <strong>전문가가 어떻게 자신의 역할을 재정의</strong>하는지 보여줍니다. 법무팀을 단순히 ‘안된다’고 말하는 부서가 아닌, 혁신을 가능하게 하는 파트너로 만들어가는 팀장님의 이야기는 법조계를 꿈꾸는 이들뿐 아니라, 조직 내 전문가 역할의 변화를 고민하는 사람들에게 해당될 수 있습니다.</p><p>변호사의 삶은 성공적인 커리어와 개인적 만족 사이에서 균형을 찾는 과정이기도 합니다. 오동훈 팀장님은 소속감과 일의 의미, 전문성과 다양성, 그리고 스트레스와 책임감 사이의 균형점을 찾아가는 과정을 솔직하게 들려주셨습니다. 사내 변호사가 어떻게 일하는지에 대한 소개를 넘어, 변화하는 세상에서 전문가로서 의미 있는 커리어를 설계하는 데 도움이 되었으면 합니다.</p><h3>콴다의 글로벌 사업을 든든히 받치는 법무팀</h3><p><strong>Q. 콴다라는 회사와, 법무팀장으로서의 역할에 대해 소개해 주세요.</strong></p><p>콴다라는 서비스로 잘 알려진 콴다 팀의 법무팀장으로 일하고 있습니다. 콴다 팀은 AI 기반 문제 풀이 검색 서비스인 콴다, 콴다 태블릿 앱을 통해 화상수업을 진행하는 ‘콴다과외’ 등 다양한 서비스를 여러 국가에서 제공하고 있습니다.</p><p>콴다의 법무팀은 지금은 저와 다른 변호사님 한 분, 2인 체제로 일하고 있습니다. 한국 본사에 100명 정도의 직원이 있고, 약 800만 MAU 정도의 글로벌 서비스를 두 명의 변호사가 관리하다 보니 업무량이 많은 편이기는 합니다. 새로운 기능 추가, 결제 방식 변경, 약관, 전자상거래, 저작권, 개인정보 등 모든 것들이 각 국가에 맞게 맞춤화가 되어야 해서 품이 많이 들기는 하죠.</p><h3>김앤장에서 스타트업으로: 변화라는 하나의 큰 줄기</h3><p><strong>Q. 김앤장을 포함하여 두 개의 로펌을 더 거쳐 콴다에 합류하시게 되셨습니다. 콴다에 합류하시게 된 계기가 궁금합니다.</strong></p><p>군법무관 시절에 어떤 직역을 택할지에 대한 고민을 많이 했습니다. 당시 ‘이코노미스트’라는 주간지를 구독하며 블록체인, AI, 자율주행, 유전자편집 같은 기술 트렌드를 접했어요. “세상이 이렇게 빨리 변하는데, 수백 년 전부터 존재해왔던 판사, 검사와 같은 직업을 택해 10–20년 정도 일하다 보면 세상이 얼마나 변해 있을까? 내가 그 변화된 세상에 적응할 수 있을까?”라는 생각이 들었습니다.</p><p>그래서 그나마 다른 조직에 비해 변화에 좀 더 가까이 있다고 생각되는 것이 로펌이었고, 그 중에서도 김앤장을 선택하게 됐습니다. 김앤장에서 근무하는 선배들을 통해서 당시 ‘핀테크’와 같은 새로 출현한 ‘기존의 규제’에서 예상하지 못했던 기술의 등장으로 인해 의뢰인들이 느끼는 어려움들을 해결해 주는 것의 보람에 대해 듣게 되었어요.</p><p>김앤장에서 근무하면서 <strong>장점도 많고 보람도 많이 느꼈습니다.</strong> 김앤장은 매우 큰 조직이지만 또 매우 수평적이고, 탈권위적이기도 합니다. 연차 높은 변호사님의 의견대로 방향이 정해지는 게 아니라 가장 합리적인 의견에 따라 방향이 정해지죠.</p><p>그럼에도 불구하고 ‘김앤장 소속’이라는 소속감으로는 가질 수 없는, ‘의뢰인’의 일을 ‘대리인’으로서 하는 구조적 특성 속에서 ‘완전한 소속감’을 가지고 일하고 싶다는 갈증이 있었습니다. 그러다가 또래의 지인들이 설립한 청출에 합류하게 되었습니다. 청출에서의 여정은 매우 즐겁고 행복했습니다. 또래 친구들과 머리를 맞대고 문제들을 해결해 나가고, 과거에 비해 많이 힘들어진 법률시장 속에서 어떻게 생존할 것인가에 대해 고민하는 건 정말 어렵고 힘들기도 하지만 즐겁기도 했어요.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*E89FVPzPNU1RgKKB" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*haLzV5W0qkniYKgr" /><figcaption>법무법인 청출에서 근무 중일 때의 프로필 사진(좌)와 현재 일상 생활에서의 사진(우)</figcaption></figure><p>그러다가 김앤장에서 알고 지내던 선배께서 연락이 왔습니다. 그분이 콴다 법무팀장으로서 제 전임자이신데요, 이 포지션을 이어받을 생각이 없냐고 하셨죠. 그 당시 업무를 하며, ChatGPT를 꽤 쓰고 있었던 시절이었고, 청출 회사 홈페이지도 제가 ‘워드프레스’라는 툴을 공부해서 디자인도 바꿔보고, 새로운 기능도 추가해보는 등 한창 tech에도 관심이 많을 때였습니다. 콴다라는 곳이 우리나라에서도 활발히 시장을 개척해 나가고 있음과 <strong>동시에 여러 나라에 진출해 있는 상황이라는 점에서 큰 매력을 느꼈습니다.</strong> 꼭 구성원이 되어 저 배를 함께 타보고 싶다는 강한 이끌림을 느껴서 오게 된 것 같네요.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*Whs9qAuhoWI048dy" /><figcaption>전임자와 콴다에서 함께 찍은 스냅 사진, 조금 더 부드러워진 분위기를 느낄 수 있다</figcaption></figure><p><strong>Q. 김앤장과 청출에서 맡으셨던 기업 법무에 대한 전문성이 콴다에서는 어떻게 도움이 되시나요?</strong></p><p>태도의 측면에서는 큰 도움이 됩니다. 지식의 측면에서는 한계가 있고요. 콴다에서는 정말 다양한 영역에 대해 접하게 됩니다. <strong>개인정보, 약관, 전자상거래, 학원법, 조세 등등 정말 많은 영역을 접하는 것이 콴다 업무의 특징입니다.</strong></p><p>변호사라는 업무가 지식을 쌓고 나서 그 지식을 계속 써먹는 직업은 아니라고 생각해요. 결론을 찾아나가는 훌륭한 방법을 체화해 나가는 직업이라고 생각합니다. 변호사의 업무는 매 이슈, 케이스마다 항상 다르다는 점이 큰 특징이라고 생각합니다. 추상적으로 볼 때는 같은 질문인 것 같다고 해도 사실관계, 배경이 다르면 다른 질문이 될 수 있죠. 답도 달라질 수 있고요. 그러다 보니 매 이슈마다 처음부터 공부한다는 생각으로 관련 법령과 법리를 연구하고 지금 상황에 적용해 보려는 자세가 요구된다고 생각해요. 김앤장과 청출에서 그러한 ‘태도’, ‘방법’에 대한 트레이닝을 할 수 있었던 것이 콴다의 사내변호사로 근무하는 것에 큰 도움이 됩니다.</p><p><strong>Q. 콴다 내에서 사내변호사로 일하면서 특별히 보람을 느꼈던 순간이 있다면 무엇인가요?</strong></p><p>‘콴다’라는 회사에서 일하는 보람이랄까, 장점에 대해 말씀드리면, 콴다는 정말 장점이 많고 매력적인 회사입니다. 에듀테크 회사라는 점에서 하루가 다르게 변화하는 AI, LLM 등과 같은 기술을 매일매일 접하며 트렌드의 변화를 접하게 되죠. 회사가 내걸고 있는 ‘교육의 평등’이라는 가치를 실현하는 일원이 될 수 있다는 것도 보람을 느끼게 합니다.</p><p>이러한 서비스들을 여러 국가에서 제공하고 있는 것도 때로는 힘들고 어렵지만, 큰 보람을 느끼게 합니다. 작년에 베트남 법인에서 근무하는 현지 직원들이 한국을 방문해서 인사를 나눈 적이 있습니다. 베트남 직원 분들이 회사의 구성원으로서 느끼고 있는 자부심, 뿌듯함, 미래에 대한 열정 같은 것들을 많이 느꼈습니다. 그때 전달받았던 에너지가 생각이 나네요. 아직은 제가 해외지사 출장을 못 가봤습니다만, 근시일 내에 꼭 해외지사에 출장을 가보고 싶습니다.</p><h3>사내 변호사와 로펌 변호사: 차이점과 선택의 기준</h3><p><strong>Q. 로펌에서 사내 변호사로 오시면서 업무 환경이나 문화에서 느끼는 가장 큰 차이점은 무엇인가요?</strong></p><p>먼저 모든 사내변호사가 동일하게 느끼지 않을 것 같다는 것을 짚고 가야 할 것 같습니다. 경험의 차이를 만드는 요인 중 중요한 것은, 질문 주신 대형 로펌과 사내 변호사라는 차이가 있고, 또 사내 변호사 중에서도 대기업이 아닌 스타트업의, 콴다의 사내 변호사라는 특성이 있어서 일반화하기에는 어려운 면이 있을 것 같아요.</p><p>그럼에도 보편적으로 느낄만한 것은, 사내 변호사의 장점은 온전히 그 회사의 편에 서서 소속감을 느끼고 업무를 할 수 있다는 점이라고 생각합니다. 앞서 얘기했던 ‘의뢰인’과 ‘대리인’의 구조가 없어지고, 당사자로서 하나의 회사에 소속감을 가지고 비즈니스를 아주 가까이서 보면서 이 회사만을 위하여 고민하고, 솔루션을 제공하게 됩니다. 따라서 그 회사가 속한 산업군이 본인이 관심이 많은 영역이라고 한다면 큰 흥미를 가질 수 있다는 것이 큰 차이가 있습니다.</p><p>다음으로는 연속적으로 계속 업무를 할 수 있다는 것이 있는 것 같습니다. 로펌 변호사일 때는 케이스별로 진행이 되다 보니, 간헐적으로 클라이언트가 변경되면서 특정 이슈, 가령 저의 경우에는 중재, 통상, 에너지 등 제 전문분야에 대해서만 답을 드리는 경우가 많습니다. 하지만 최적의 답변을 드리기 위해서는 지금 이 회사가 어떤 맥락에서 사업을 해왔고, 과거와 현재는 어떻게 달라졌고 미래에는 어떤 업무를 추진할 계획인지 등 전반적인 배경에 대해 충분히 알아야 하는데, 시간이나 여러 한계로 인해 그 부분에서 ‘내가 과연 제대로 답을 드리고 있는 것인가?’에 대해 답답함이 있었던 것 같습니다. 하지만 회사에 소속된 이후에는 온전히 이 회사의 정보를 알고, 고민하고, 연속선상에서 맥락을 바탕으로 판단할 수 있다는 것이 다른 것 같습니다.</p><p>이제 스타트업이라서 다르게 느껴지는 특성이 있는데, 콴다는 평균 연령대가 굉장히 낮고 유연한 조직 환경을 가지고 있고, 구성원들 역시 유연한 사고를 가지고 있어요. 그래서 ‘법무팀이 이런 것을 왜 해요?’라거나, 서로 일을 미루는 것이 아니라, ‘제가 챙길게요’라는 것이 미덕인 분위기입니다. 이런 문화가 저에게는 큰 장점으로 다가왔습니다. 스타트업은 결정과 실행 속도가 빠르고, 새로운 시도를 두려워하지 않는 문화가 있어 제 성향과도 잘 맞았던 것 같습니다.</p><p><strong>Q. 사내 변호사와 로펌 변호사의 업무 스타일 차이를 운동선수에 비유해 주셨는데요, 조금 더 들어보아도 될까요?</strong></p><p>사내 변호사와 로펌 변호사와의 가장 큰 차이는 전문성의 깊이와 범위에 있다고 봅니다. 축구로 비유하자면, 로펌 변호사는 코너킥이나 페널티킥 같은 특정 기술만 계속 연마하는 선수라고 할 수 있어요. 그 특정 영역에서는 누구보다 뛰어나게 되죠. 반면 사내 변호사는 코너킥도 차고, 수비도 하고, 골키퍼도 하는, 다양한 역할을 수행하는 선수에 가깝습니다.</p><p>이런 차이 때문에 사내 변호사로 있다 보면 전문성에 갈증을 느낄 때가 있어요. 특정 분야를 깊이 파고들어 전문가가 되고 싶은데, 사내 변호사는 제너럴리스트의 성격이 강하거든요. 또한 법률가라는 느낌보다 회사원이라는 생각을 더 많이 하게 되는 경우도 있습니다. 이런 특성을 좋아하면 사내 변호사가 매력적인 직업이 될 수 있지만, 정통 법률가로서의 길을 원한다면 다시 로펌으로 돌아가고 싶어질 수도 있어요.</p><p>이런 전문성의 차이는 어쩔 수 없다 보니, 별도로 시간을 내서 공부하려고 노력해요. 논문을 찾아보거나 주석서, 스타트업계에 대한 법적 규제 동향을 살펴보는 식으로요. 하지만 많은 법조인들이 말하듯, 글이나 서적만으로 공부하는 것은 한계가 있어요. 실제 구체적인 케이스를 다뤄봐야 진짜 실력이 늘어나는 법이니까요.</p><p>그래서 저는 특별히 사내 변호사로서의 장점에 더 집중하려 합니다. 다양한 영역을 경험하면서 넓은 시야를 가질 수 있고, 비즈니스에 대한 이해도 높일 수 있어요. 예를 들어 PG 업체들과 결제 관련 계약을 검토하면서 결제 시스템이 어떻게 작동하는지 배우고, 마케팅 계약을 다루면서 온라인 마케팅의 비즈니스 모델과 과금 구조를 이해하게 됩니다. 이런 지식 없이는 계약서를 제대로 검토할 수 없기 때문에, 결국 법률 외적인 부분에서도 계속 배우게 되는 것이죠.</p><p><strong>Q. 역시 인생은 실전으로 배우는 것이 많은 것 같아요. 법무팀장님이 보실 때는 어떤 사람에게 사내 변호사가 적합할까요?</strong></p><p>지적 호기심이 왕성하고 다양한 영역을 경험하고 싶은 사람에게 적합한 것 같아요. 한 분야에 깊이 몰입하기보다 여러 다른 분야를 넓게 경험하는 것을 즐기는 사람이라면 사내 변호사로서 만족감을 느낄 가능성이 높습니다.</p><p>특히 콴다 같은 경우는 ‘이것만 하면 돼’라고 명확하게 정해진 업무 범위가 없어요. 이게 답답하게 느껴질 수도 있지만, 반대로 하고 싶은 일이 많은 사람에게는 기회가 됩니다. 다양한 비즈니스에 대한 호기심이 왕성하고, 법률 전문가로서도 여러 영역의 경험을 해보고 싶은 분이라면 사내 변호사 역할이 매우 보람찰 것입니다.</p><p>한편 사내 변호사에서 로펌으로 돌아가는 경우도 종종 있어요. 주된 이유 중 하나는 앞서 언급한 전문성의 문제인 것 같습니다. 특정 분야의 전문가로 깊이 있게 성장하고 싶은 욕구가 있는데, 사내 변호사 역할에서는 그 욕구가 충족되지 않을 수 있거든요.</p><p>또 다른 이유는 회사 내 성장 가능성과 관련이 있을 것 같아요. 로펌은 파트너 변호사로 승진하는 경로가 비교적 명확하고, 전문성을 인정받으면 그에 맞는 보상과 성장 기회가 주어지는 경우가 많습니다. 반면 기업에서는 법무팀의 규모가 제한적이고 승진 기회도 한정적일 수 있어요.</p><p>마지막으로, 사내 변호사는 한 회사의 문화와 방향성에 더 많이 영향을 받게 됩니다. 그 회사의 비전이나 문화가 자신과 맞지 않는다고 느끼면, 다시 로펌으로 돌아가 여러 회사의 다양한 이슈를 다루는 것이 더 맞다고 판단할 수도 있죠.</p><h3>효과적인 팀 운영과 커뮤니케이션 방법</h3><p><strong>Q. 스타트업의 법무팀장을 맡고 계신데, 지금은 스스로 어떤 역량이 가장 중요하다고 생각하시나요?</strong></p><p>로펌 변호사와 사내 변호사의 가장 큰 차이 중 하나는, 로펌 변호사는 ‘법’적인 영역에 대해서만 고려하면 되지만, 사내 변호사는 비즈니스 관점에서도 고려해야 한다는 점입니다. 회사 경영자의 관점에서 사고할 수 있는 능력이 필수적이죠. 특정 법적 이슈가 주어졌을 때, 대표님들이나 각 그룹장님들이 어떤 생각을 할지 입장을 바꾸어 생각하는 훈련이 필요합니다.</p><p>또한 너무 엄격하게 법이론적인 관점에서만 답을 드리면, 현업에서는 좌절스러운 상황이 발생할 수 있어요. 법적 리스크가 우려되어도 단순히 ‘안됩니다’라는 결론만 전달하는 것이 아니라, 제3의 대안을 같이 고민하고, 최대한 ‘일이 되도록’ 할 수 있는 것이 특히 스타트업의 사내 변호사에게는 중요한 덕목입니다.</p><p>예를 들어, 법적 리스크가 우려되는 상황에서도 “이 방향으로는 어렵지만, 이런 방식으로 접근하면 목표를 달성하면서도 리스크를 줄일 수 있을 것 같다”는 식으로 대안을 제시하는 것이 중요합니다. 이렇게 하면 현업도 법무팀을 단순히 ‘일을 막는 부서’가 아니라 ‘함께 해결책을 찾는 파트너’로 인식하게 됩니다.</p><p>저도 콴다에 처음 들어온 초기에는 회사의 조직과 구체적인 서비스 내용들을 파악하는 것이 가장 높은 우선순위였습니다. 콴다는 다양한 서비스를 제공하고 있는데요, 그 구체적인 서비스가 어떻게 되는지를 정확하게 파악하지 못하면, 특정 법적 쟁점에 있어서도 비즈니스 관점을 고려하지 못해 엉뚱한 답을 내리게 됩니다. 직접 콴다 앱을 깔고, 이것저것 써보기도 하고, 콴다과외에서 수업이 어떻게 이루어지는지 등에 대해서 파악하는 데에 꽤 많은 시간이 소요되었지만 이것이 선행되지 않으면 잘못된 판단을 할 수 있다는 점에서 꼭 필요한 과정이었다고 생각합니다. 사내 변호사라면, 법과 비즈니스 양면을 다 이해하려는 노력이 꾸준히 필요한 것 같아요.</p><p><strong>Q. 변호사시면서 동시에 법무팀장이시기도 한데, 팀장으로서는 어떤 리더십 스타일을 가지고 계신가요?</strong></p><p>최대한 구성원의 자율성을 존중하는 방향으로 팀을 운영하려고 합니다. 예를 들어, 어떤 질문이 들어왔을 때 여러 가지 답을 제시할 수 있는 상황에서, 구성원이 제시한 방향에 동의한다면 제가 처음에 생각했던 방향과 다르더라도 존중하려고 합니다. 구성원이 더 그립(grip)을 갖고 만족감을 느낄 수 있게 하는 것이 장기적으로 팀에 더 도움이 된다고 생각하거든요. 물론 명백히 다른 방향으로 가야 한다고 생각될 때는 충분한 토론과 상의를 통해 함께 결정합니다.</p><p>콴다에서는 IM(Iteration Meeting)이라는 제도가 있어서, 팀원이 본인의 팀 리드와 정기적으로 업무 진행 상황, 컨디션 체크 등을 1:1로 논의하는 시간을 갖습니다. 이 시간을 통해 격의 없이 이야기할 수 있는 분위기를 만들고, 특히 이슈에 대한 접근 방식이나 결론, 논리 등을 자유롭게 얘기할 수 있는 수평적인 환경을 조성하려고 노력합니다.</p><p><strong>Q. 지금 포지션 중 하나가 비었다고 들었습니다. 혹시라도 같이 일하시는 분이 본다면, 콴다는 어떤 점이 좋고, 어떤 분이 적절한지 얘기해 주셔도 될까요?</strong></p><p>콴다에서는 앞서 얘기했던 사내변호사로서의 강점을 발휘하기 좋은 구조입니다. 많은 부서의 슬랙(메신저)의 채널들이 열려 있어서, 누구라도 들어가서 지금 이 팀에서 뭘 하고 있고, 뭘 의논하고 있고, 앞으로 어느 방향으로 회사가 가겠구나라는 것에 대한 접근이 가능합니다. 때문에 비즈니스에 대한 호기심이 왕성하고, 법률 전문가로서도 다양한 영역과 로케일에 대한 경험을 해보고 싶으시면 콴다만큼 좋은 곳이 없을 것 같습니다.</p><p><strong>여기만큼 일하면서 ‘학교를 다니거나 동아리 생활을 하는 느낌’을 받는데 보상도 주어지는 만족감을 느낄 수 있는 조직이 전 세계, 대한민국은 물론이고 전 세계에 과연 얼마나 될까라는 생각을 하게 됩니다.</strong> 그런 부분에서 저는 너무 만족하고 즐겁게 생활을 하고 있어요. 요약하면 적극성과 다방면에 지적 호기심이 왕성한 분이 오신다면 정말 만족스럽게 업무를 하실 수 있을 것 같습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*JXeBppLu3fkcKJyS" /><figcaption>콴다에서의 일이 행복하다고 말할 때 주저함이 없었다.</figcaption></figure><p><strong>Q. 법무팀의 고객인 현업 부서에게 팁을 주자면, 효과적인 법적 검토 의뢰를 위해서는 어떻게 하는 것이 좋을까요?</strong></p><p>마치 AI에 프롬프트를 잘 작성하는 것과 비슷해요. 최대한 배경에 대해 구체적으로 설명해 주시고, 의뢰하시는 입장에서 우려되는 지점들을 함께 공유해 주시는 게 중요합니다. 법률 전문가가 보기에 문제가 되는 부분과 의뢰인이 걱정하는 부분이 항상 일치하지는 않지만, 많은 경우 직관적으로 찝찝한 부분이 실제로 법률적 이슈가 될 가능성이 높거든요.</p><p>계약서 검토를 예로 들면, 그냥 ‘이 계약서에 문제가 있는지 봐주세요’라고 요청하면 법률가는 문구만 보고 검토할 수밖에 없어요. 하지만 ‘이 계약은 이런 배경에서 체결하려고 합니다’, ‘이 부분이 우려됩니다’, ‘저희는 이 관계를 장기적으로 발전시키고 싶습니다’ 같은 맥락을 알려주시면 훨씬 더 실질적인 도움을 드릴 수 있습니다.</p><p>때로는 ‘이 계약 자체는 금액이 작고 상대방이 과한 요구를 하고 있지만, 지금은 관계를 트는 게 중요해서 심각한 리스크만 없다면 진행했으면 합니다’와 같은 비즈니스적 판단도 알려주시면 더 적절한 검토가 가능해집니다.</p><h3>스트레스 관리 — 적정 수준의 책임감 찾기</h3><p><strong>Q. 변호사로서 일하면서 스트레스나 번아웃을 어떻게 관리하시나요?</strong></p><p>분쟁 사건을 맡았을 때 특히 스트레스가 컸어요. 의뢰인의 상황에 너무 감정이입을 하면서 마치 제 일처럼 느끼게 되었거든요. 특히 형사 사건의 경우 결과에 따라 한 사람의 인생이 완전히 바뀔 수 있는데, 내가 놓친 부분이나 실수로 의뢰인에게 불이익이 갈까 봐 부담감이 컸습니다.</p><p>저는 그 부담을 항상 과하게 느껴 왔던 편이었어서, 지금은 사내 변호사로서 그런 부담을 조금 의식적으로 내려놓으려고 노력해요. 책임감은 적정 수준을 유지하는 게 중요하다고 생각합니다. 너무 없으면 중요한 일을 소홀히 할 수 있지만, 과하면 결국 소진되고 말거든요.</p><p>저는 ‘누가 와도 똑같은 결론에 도달했을 것이다’라고 생각할 수 있을 정도의 검토를 했다면, 그 뒤에는 의식적으로 생각을 내려놓으려고 합니다. 팀원들과 충분히 논의하고 최선의 결론을 도출했다면, 만약 결과가 나쁘게 되더라도 그건 내 탓이 아니라 누구라도 피할 수 없었던 일이라고 생각하는 연습을 많이 해요.</p><p><strong>Q. 그럼에도 여전히 콴다에서는 회사의 명운을 좌우할 수 있는 중요한 결정에 참여하게 되는데, 그런 부담감을 어떻게 다루시나요?</strong></p><p>맞아요. 여전히 제가 잘못 판단하면 회사에 큰 영향이 올 수 있고, 심한 경우 회사가 문을 닫아야 할 정도의 리스크가 생길 수도 있는 상황들이 있습니다. 하지만 여기서 중요한 차이점은 분쟁과 달리 더 넓은 맥락에서 일을 바라볼 수 있다는 것이에요.</p><p>로펌에서 분쟁 사건을 맡을 때는 그 사건 자체에만 집중하게 되고, 결과에 따라 고객의 인생이 크게 바뀔 수 있다는 압박감이 컸어요. 반면 사내 변호사로서는 회사의 전체적인 비전과 목표를 알고, 그 안에서 법적 리스크를 관리하는 역할을 하다 보니 더 균형 잡힌 시각을 유지하기가 수월한 것 같습니다.</p><p>또한 콴다에서는 의사결정을 혼자 하지 않고 팀으로 함께 내린다는 점도 큰 도움이 됩니다. 어떤 중요한 법적 이슈가 생기면 CEO나 다른 임원들과 함께 논의하고, 각자의 전문 영역에서 의견을 모아 최선의 결정을 내리죠. 이렇게 집단 지성을 활용하면 결정에 대한 부담감도 나눌 수 있고, 더 나은 결과를 얻을 가능성도 높아집니다.</p><p><strong>Q. 그러면 스트레스 관리를 위해 팀 구성원들과 어떻게 협업하시나요?</strong></p><p>팀 구성원의 존재가 큰 도움이 됩니다. 혼자서 모든 결정을 내려야 한다면 훨씬 외롭고 부담스러울 텐데, 함께 머리를 맞대고 고민하면 더 나은 결론에 도달할 수 있어요. 집단 지성의 힘을 빌리면 오류 가능성도 훨씬 낮아지죠.</p><p>때로는 외부 전문가의 의견을 구하기도 합니다. ‘이런 상황에서 이런 결론을 내렸는데 합리적이냐’라고 물어보면 객관적인 시각을 얻을 수 있어요. 적절한 자문을 구하고 공동의 의사결정을 하는 것이 개인의 부담을 줄이는 좋은 방법입니다.</p><h3>후배 법조인을 위한 조언</h3><p><strong>Q. 사내 변호사로서 앞으로의 커리어 목표는 무엇인가요?</strong></p><p>사실 저는 지금 콴다의 변호사로서 매우 만족하고 있어요. 이 회사에서 일하는 것이 마치 넷플릭스 시리즈를 정주행하는 느낌이랄까요? 시즌이 계속 나왔으면 좋겠다고 바라는 그런 상황입니다.</p><p>미래 계획을 말씀드리자면, 처음엔 법무를 벗어나 다른 영역으로 확장해볼까 하는 생각도 있었어요. 하지만 실제로 와서 일해보니 오히려 법률 영역에서의 흥미가 더 커졌습니다. 특히 회사 내에서 리걸 베이스를 가지고 있는 제가 고유하게 기여할 수 있는 부분들이 많다는 것을 깨달았어요.</p><p>앞으로는 회사와 함께 성장하면서 새로운 역할을 맡아볼 수도 있고, 법무 영역을 넘어서는 제안을 받게 될 수도 있을 것 같습니다. 하지만 지금은 5년, 10년 후를 정확히 계획하기보다는 현재에 집중하고, 내게 기대되는 일에 최선을 다하다 보면 자연스럽게 길이 열릴 거라고 생각해요. 일단 콴다의 IPO가 저희 모두의 중간 목표이지만, 그 과정 자체를 즐기고 있습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*dqKYUJrE8PSn20_Q" /></figure><p><strong>Q. 만약 지금 로스쿨을 다니고 있는 20대가 본다면, 커리어를 위해서 어떤 조언을 해주실 수 있을까요?</strong></p><p>우선, 현실적인 이야기부터 해보자면, 법조계 시장은 이전보다 훨씬 더 치열해졌어요. 변호사 4만 명 시대를 눈앞에 둔 상황에서, 여러분이 처음 법조인을 꿈꿨을 때 가졌던 기대 — 경험할 수 있는 것들, 받을 수 있는 보상 — 이런 것들을 대부분 내려놓아야 합니다. 오히려 기대치가 낮을수록 직업에 대한 만족도는 높아지는 것 같아요.</p><p>예전에는 보상 측면에서 법조인이라는 직업이 많은 장점을 가졌습니다. 공직은 명예를, 민간 부문에서는 경제적 보상을 누릴 수 있었죠. 하지만 지금은 그런 것들이 많이 약화되었어요.</p><p>대신 변호사라는 직업의 가장 큰 장점은 ‘확장성’입니다. 정년이 없고, 개인으로서 자신만의 브랜드를 구축할 수 있다는 점이죠. 변호사 자격증을 취득하는 순간부터, 여러분은 자신을 하나의 브랜드로 키워나가는 싸움을 시작하게 됩니다.</p><p>설령 법원, 검찰처럼 평생 직장으로 볼 수 있는 곳에 가더라도, 그 안에서 다른 구성원들과 어떤 점에서 차별화된 가치를 제공할 것인지 고민해야 합니다. 차별화되지 않으면 생존 자체가 어려운 시대가 되었어요. 따라서 ‘내가 어떻게 브랜딩할 것인가’, ‘다른 사람들과 어떻게 차별화할 것인가’를 끊임없이 고민해야 합니다.</p><p>또 하나 기억해야 할 점은, 변호사는 공부와 뗄 수 없는 직업이라는 것입니다. 법리와 제도는 계속 변화하고, 그에 맞춰 새로운 정보를 끊임없이 습득해야 합니다. 법은 특정 현상을 규율하기 위해 존재하기 때문에, 변화하는 세상과 기술에 대한 이해 없이는 법을 제대로 적용할 수 없죠. 따라서 지적 호기심이 많은 사람에게는 정말 좋은 직업이 될 수 있지만, 공부하는 것을 싫어한다면, 솔직히 잘못 온 겁니다.</p><p>마지막으로, 자신이 무엇을 좋아하는지 찾는 것이 중요합니다. 대형 로펌에서는 ‘프랙티스’와 ‘산업군’으로 업무를 분류하는데, 자신의 관심사와 연결된 분야를 찾으면 훨씬 만족도가 높아집니다. 예를 들어 자동차에 관심이 많다면, 자동차 회사의 사내 변호사나 자동차 관련 사건을 맡는 로펌 변호사가 되면 일이 더 즐겁겠죠. 제 경험에서도 그랬어요. 주류 회사 클라이언트를 맡았을 때, 캔을 흔들면 거품이 나는 원리까지 공부하게 되었는데, 이런 경험은 업무에 즐거움을 더해줍니다. 친구들 만나서 ‘나 이거 어떻게 되는지 알아’라고 말할 수 있는 것도 소소한 재미죠.</p><p>많은 선배들이 말씀하시듯, 자신의 성향과 잘 맞는 분야를 찾는 것은 정말 행운입니다. 여러분이 무엇을 좋아하는지, 어떤 일을 할 때 즐거운지 깊이 고민한다면, 보다 만족스러운 법률 커리어를 구축할 수 있을 겁니다.</p><p><strong>Q. 이제 막 법무팀에 합류한 후배 사내변호사들에게 해주고 싶은 조언이 있으신가요?</strong></p><p>매순간 맡은 일에 최선을 다했으면 합니다. 저도 ‘이거 내가 왜 해야 되지? 나랑 아무 상관 없고, 앞으로도 아무 도움이 안 될 것 같은데’라는 생각을 많이 했습니다. 하지만 ‘인생 어떻게 될지 모른다. 공부한 거 경험한 거 언젠가 다 쓸모가 있다’는 어른들 말씀이 정말 와닿습니다.</p><p>변호사로서의 커리어를 발전, 확장할 수 있는 기회가 종종 혹은 가끔 오게 되는데요, 결국 그런 기회는 매사에 최선을 다하는 사람에게 주로 다가오고, 그런 사람들이 기회를 자기 것으로 만들 수 있는 능력도 갖게 된다는 생각입니다.</p><p>사법시험을 준비하던 시절, 사법연수원, 군법무관 혹은 김앤장을 다니던 시절에 제가 콴다와 같은 에듀테크 기업에 법무팀장으로서 일하게 될 것이라고는 전혀 상상해 본 적이 없습니다. 저는 여기서 일할 수 있어서 정말 즐겁고 만족스럽습니다. 커리어의 전개도, 어떻게 살아야 만족하고 행복한지에 대해서도 결코 속단할 수 없는 것 같아요. 매 순간 즐기면서 열심히 살아보면 만족과 행복에 가까워지는 것 같습니다.</p><p>🌏 글로벌 Top 교육 앱 QANDA(콴다)를 함께 만들어 갈 분들을 기다리고 있습니다! 법무라는 영역에서 단순한 컴플라이언스의 틀을 넘어, <strong>비즈니스에 기여하는 전략 파트너로서의 역할</strong>을 함께 고민하고 실현하고 싶은 분이라면 언제든지 저희의 문은 열려 있어요. :)</p><p>👉 자세한 사항은 <a href="http://bit.ly/3pVjlmR">채용 사이트</a>를 통해 확인해주세요.</p><p>👉 해당 인터뷰는 프릭스(Prix.im)의 동의를 얻어 공유되었습니다. (<a href="https://www.prix.im/blog/posts/corporatelawyer-01">인터뷰 원문</a>)</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=65f9d9bf2054" width="1" height="1" alt=""><hr><p><a href="https://blog.mathpresso.com/%EC%A7%80%EC%A0%81-%ED%98%B8%EA%B8%B0%EC%8B%AC%EC%9D%B4-%EC%9D%B4%EB%81%84%EB%8A%94-%EC%BB%A4%EB%A6%AC%EC%96%B4-%EC%97%AC%EC%A0%95-%EC%BD%B4%EB%8B%A4-legal-division-head-65f9d9bf2054">지적 호기심이 이끄는 커리어 여정 | 콴다 Legal Division Head</a> was originally published in <a href="https://blog.mathpresso.com">Team QANDA</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[콴다 팀의 엔지니어링 조직문화]]></title>
            <link>https://blog.mathpresso.com/%EC%BD%B4%EB%8B%A4-%ED%8C%80%EC%9D%98-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4%EB%A7%81-%EC%A1%B0%EC%A7%81%EB%AC%B8%ED%99%94-60df9136e050?source=rss----35e40785d286---4</link>
            <guid isPermaLink="false">https://medium.com/p/60df9136e050</guid>
            <category><![CDATA[systemqa]]></category>
            <category><![CDATA[product]]></category>
            <category><![CDATA[mobile]]></category>
            <category><![CDATA[febe]]></category>
            <category><![CDATA[culture]]></category>
            <dc:creator><![CDATA[Bruce Oh]]></dc:creator>
            <pubDate>Tue, 04 Jun 2024 07:39:18 GMT</pubDate>
            <atom:updated>2024-06-04T09:06:15.508Z</atom:updated>
            <content:encoded><![CDATA[<h4>좋은 조직문화를 위해 무엇을 하나요?</h4><p>안녕하세요, 콴다 Software Engineer Bruce 입니다. 오늘은 많은 분들이 궁금해하셨던 콴다 팀의 엔지니어링 조직문화 레이드에 관한 이야기를 해보려고 합니다.</p><h4><strong>왜 조직문화가 중요한가요?</strong></h4><p>주변 동료나 다른 개발자/엔지니어들과 커리어에 대해 토론할 때 자주 등장하는 주제가 있습니다. 그것은 바로 <strong>‘성장&#39;</strong>입니다. 많은 엔지니어가 개인적인 성장에 대해서 고민하고, 각자의 분야에서 성장하는 방법을 찾으려 많은 노력을 기울입니다.</p><p>그렇다면 엔지니어가 말하는 ‘성장’이란 무엇일까요? 남들보다 뛰어난 엔지니어링 실력을 기르는 것일까요? 직급이 상승하는 것일까요? 아니면 급여나 조건이 향상되는 것일까요? 아마도 이 모든 것을 포함할 수 있겠지만, 동료나 다른 엔지니어들과 이야기를 나누어 보면 그들에게 성장은 꼭 커리어에 대한 결과적 보상만을 의미하지 않았습니다. 오히려 더 많은 엔지니어링 문제를 해결하고, 어떻게 하면 이 분야에 지속적으로 기여할 수 있을까를 더 많이 고민하고 있었습니다.</p><p>제가 이해한 엔지니어의 성장은 ‘다음 방향을 찾고 이를 수행할 수 있는 원동력’에 더 가깝습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PTm7ZWyZISoVIXXiEo2cWw.jpeg" /></figure><p>사실 엔지니어가 느끼는 성장에 관한 열망은 현업에 계신 분들뿐만 아니라, 엔지니어가 되기를 희망하는 준비하시는 분들도 대부분 비슷하게 표현합니다. 학습을 하거나 자신에게 주어진 업무를 수행한 후 시간이 지나면 더 중요하고 새로운 일들을 해보고 싶지만, 그 일을 수행하기 위한 성장은 혼자서는 부족할 때가 많다고 해요. 그래서 성장에 관한 어려움을 함께 나눌 수 있는 동료와, 고민을 같이 해결해 나갈 좋은 방향성을 가진 조직에서 성장을 기대하고 바라고 있습니다.</p><p>콴다 팀에서는 이러한 엔지니어들의 성장에 대한 열정을 반영하여, 조직 내에서 각 개인의 동기를 향상시킬 방법 중 하나로 ‘좋은 엔지니어링 조직문화’를 구축하는 것을 목표로 하고 있습니다. 이 목표를 위해 <strong>엔지니어링 조직문화 레이드</strong>를 2023년에 개최하게 되었습니다. (<a href="https://blog.mathpresso.com/how-qanda-team-solves-problems-together-backend-frontend-team-raid-dev-culture-5a5ba7419cd">콴다 팀의 레이드 문화란</a>?)</p><h4><strong>좋은 조직문화를 위해 무엇을 해야 할까요?</strong></h4><p>“좋은 조직문화를 위해 무엇을 해야 하나요?”라는 질문에 대해, 사실 저도 한마디로 대답하기는 쉽지 않습니다. 좋은 조직문화의 기준은 조직의 성향과 상황에 따라 다를 수 있기에 모두가 말하는 ‘좋은 정도(正道)’는 있을지 몰라도, 모두에게 맞는 한 가지 방법은 없다고 생각해요. 그래서 ‘좋은 조직문화가 무엇이냐’보다는 ‘우리가 좋은 조직문화가 무엇인지 정의하자’가 레이드에서 시작한 첫 과제였습니다. 마침 구글에서 DORA 리포트를 내놨던 시점이기도 했습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HhSqjJVX7td_OLkhW8qkRQ.png" /></figure><p>조직문화 레이드에서 이뤄야 할 목표는 시작과 동시에 명확했습니다.</p><ul><li>좋은 조직문화가 무엇인지 연구해 볼 것</li><li>우리에게 필요한 부분이 무엇인지 찾아보고 개선해 볼 것</li></ul><h4><strong>좋은 조직문화란?</strong></h4><p>저희가 처음 접근했던 방법은 DORA 리포트에서 나오는 내용들을 점검해 보는 것이었습니다. <a href="https://dora.dev/">DORA 리포트</a>는 Devops Research And Assessment의 약자로, 언뜻 보면 DevOps 에 관한 이야기로만 보일 수 있지만, 실제로는 조직이 가지고 있는 수행능력(capability)와 성능(performance) 에 대한 깊이 있는 고찰을 중점적으로 다룹니다. 그래서 리포트의 첫 부분에 이런 말이 있었습니다.</p><blockquote>Establish a healthy Culture: Culture is foundational to building technical capabilities, igniting technical performance, reaching organizational performance goals, and helping employees be successful. Teams with generative cultures have 30% higher organizational performance.</blockquote><p>즉, 건강한 조직 문화를 구축하는 것이 중요하며, 이러한 조직은 30% 이상의 성과를 낼 수 있다고 합니다. 이와 같이 DORA는 콴다 팀이 목표로 하는 고수준의 조직 문화에 대한 많은 내용을 담고 있었습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4FFrMSuqUN0h0vJPhhRG-Q.png" /></figure><p>레이드에 모인 구성원들은 이런 내용의 DORA 리포트를 연구하면서 여기서 나왔던 저희에게 필요한 진단 내용들을 구체화하고 개선점을 위해 해야 할 일들을 찾기 시작했습니다.</p><h4><strong>Well-being, 그리고 인큐베이팅</strong></h4><p>DORA 리포트에는 많은 기술적 지표가 있지만, 개발자의 웰빙(well-being)에 관련된 항목들도 포함되어 있습니다. Well-being은 사전적 의미로 ‘the state of being comfortable, healthy, or happy’를 뜻합니다. DORA 리포트는 개발자들의 웰빙에 신경 써야 하며, 향상된 개인의 웰빙이 조직의 퍼포먼스와 근로자들의 근속에 많은 영향을 끼친다고 발표했습니다. 특히 웰빙에서 다음과 같은 항목들이 강조됩니다:</p><ul><li>Burnout</li><li>Productivity</li><li>Job satisfaction</li></ul><p>이 세 가지는 개발자들이 단기 또는 순간에 최고의 성과를 내는 것보다는 지속적으로 좋은 성과를 유지하는 데 초점이 맞춰져 있습니다. 그래서 콴다 팀에서도 개발자들의 웰빙의 세 지표에 주의를 기울이고 향상하기 위해 다양한 노력을 하고 있습니다.</p><p>그 노력 중 하나가 새로 팀에 합류한 인원을 위한 인큐베이팅 프로그램입니다. 처음 회사에 입사하면 ‘여기는 어떻게 개발하지?’ 또는 ‘여기의 시스템은 어떻게 구성되어 있을까?’를 파악하는 데 많은 노력이 필요합니다. 특히 마이크로 서비스를 주로 설계하고 운영하는 회사에서는 복잡한 시스템을 온보딩하는 데 많은 시간이 걸릴 수 있습니다. 신규 입사자는 시작의 설렘보다 많은 부담을 느낄 수 있습니다. 콴다 팀에서는 이 문제를 해결하고 온보딩을 돕기 위해 ‘InQbator’라는 프로그램을 운영하고 있습니다.</p><p>‘InQbator’는 GitHub Repository로, 이 Repository를 Clone하여 코드를 추가하며 해결하는 여러 개의 과제로 구성되어 있습니다. 과제 설명은 의도적으로 살짝 불친절하게 작성되어 있습니다. 그 이유는 신규 입사자가 콴다 팀의 엔지니어링 문서들을 검색해 가면서 답을 찾아내기를 기대하기 때문입니다. 이렇게 함으로써, 비슷한 문제를 마주쳤을 때 스스로 해결법을 찾아가는 방법을 배울 수 있습니다. 또한 과제를 수행할 때마다 PR(GitHub Pull Request)을 생성하여 기존의 개발자들이 함께 리뷰하고 의견을 나누는 과정을 거치게 됩니다. ‘InQbator’의 여러 과제를 완료하면 실제 업무와 유사한 프로세스를 간접적으로 경험할 수 있습니다. 궁극적으로는 신규 입사자가 업무에 더 수월하게 온보딩할 수 있기를 기대하고 있습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*IgZG5ma1aKIDEsnuPs1GFQ.png" /></figure><p>이 인큐베이팅 프로세스에서 자랑하고 싶은 점이 또 있습니다. 본인이 진행한 프로젝트에서 ‘이런 부분이 더 있으면 좋겠다’고 느낀 점이 있다면 이를 스스로 추가할 수 있습니다. 다음에 신규 입사자가 오면 내가 제시한 과제들에 대해 피드백을 줄 수 있는 것이지요. 이렇게 콴다 팀은 지속적으로 발전하고 협력하는 환경을 조성하여, 모든 구성원이 성공할 수 있도록 돕고 있습니다.</p><h4><strong>적극적이고 빠른 코드 리뷰를 지향합니다.</strong></h4><p>코드 리뷰는 코드 작성자 이외의 사람들이 코드를 리뷰하는 프로세스이며, 이는 더욱 안정적인 소프트웨어를 만들기 위해 필수적인 절차입니다. 콴다 팀에서도 코드 리뷰의 중요성을 이해하고 있습니다. 그래서 코드 리뷰 프로세스를 강화하기 위해, 엔지니어링 조직은 모두에게 코드 리뷰를 작성하기를 독려하고, 더 나아가 코드 리뷰로 인한 업무 수행의 병목을 줄이고 문화를 더욱 활성화하고자 이 프로세스가 빠르게 진행되기를 권장하고 있습니다.</p><p>이를 위해 저희는 빠른 코드 리뷰 프로세스를 장려하기 위해 Slack Bot을 이용하고 있습니다. 이 봇의 이름은 ‘리뷰 좀비’입니다. 왜 좀비냐고 물어보신다면, 그만큼 중요하기 때문에 리뷰가 진행되지 않으면 좀비가 무섭게 일어나서 알려준다고 생각하며 이름을 지었습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/774/1*hDTTIAlbL3pkeYGRox9WOw.png" /></figure><p>콴다 팀의 리뷰 좀비는 다음 프로세스로 동작합니다.</p><ol><li>Notification: 내가 리뷰어로 할당된 GitHub PR(Pull Request)이 생성되면 봇이 Slack을 통해 리뷰어에게 할당된 PR을 즉시 알려줍니다.</li><li>Reminder: 리뷰가 24시간 이내에 진행되지 않으면 봇이 다시 한번 알람을 보내며 코드 리뷰를 독려합니다.</li><li>Feedback: 추가로 필요한 기능들은 GitHub Issues를 통해 개선하면서 더 좋은 문화를 만들기 위해 노력하고 있습니다.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/968/1*PVevsrr-h_LI9OzlKtSWdA.png" /></figure><p>이렇게 코드 리뷰를 진행하면서, 이 프로세스가 버그 감소, 지식 공유, 코딩 컨벤션 및 스타일 유지 등 개발자들이 협력하여 일하는 문화를 향상시킬 수 있기를 기대하고 있습니다.</p><h4><strong>콴다 팀의 문서들</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CcXvRije0MrwO2nzd2qF-A.png" /></figure><p>도서관에서 필요한 책을 찾기 위해 고생한 적이 있으신가요? 저도 가끔 도서관에 가면 먼저 검색을 하고, 해당 책이 있는 칸에서 한참 동안 책장을 서성이며 찾으려 했던 경험이 있습니다. 마찬가지로, 콴다 팀도 오랜 시간 동안 엔지니어링 관련 업적을 쌓아오면서 많은 소프트웨어와 그에 대한 문서들이 축적되었습니다. 물론 일부는 업데이트가 필요하기도 했지만, 일부는 모두가 빠르게 검색해야 하는 문서도 있었습니다. 하지만 이 문서들에 대한 검색 또는 접근성은 도서관의 책처럼 항상 쉽지 않았습니다. 서로에게 ‘그 문서 어디에 있나요?’라고 물어본 적도 많았습니다. 이를 해결하기 위해 엔지니어링 조직문화 레이드에서 문서의 위치와 양식을 좀 더 직관적으로 개선하는 작업을 진행했습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1D_lTPp239wjkPGO7cdQAg.png" /></figure><p>예를 들어, 소프트웨어 관련 문서들은 대부분 GitHub으로 이관했습니다. 이렇게 하면 소프트웨어의 상세한 비즈니스 로직을 이해하기 위해 별도의 도구(Notion, Wiki 등)를 찾아야 하는 번거로움을 줄일 수 있습니다. 또한, 개발하면서 변경사항들도 PR에서 문서 내용을 추가하라는 요청을 쉽게 할 수 있게 되었습니다. 더 나아가 Badge를 통해 이 문서에 연결된 Quick-Link나 애플리케이션의 상태를 한눈에 볼 수 있도록 문서 시스템을 개선했습니다.</p><h4><strong>구글의 </strong><a href="https://yiou.me/blog/posts/google-go-link"><strong>Go Link</strong></a><strong> 문화를 아시나요?</strong></h4><p>구글 직원들은 go-link를 정말 많이 쓴다고 알려져 있습니다. go-link는 URL에 go로 시작하는 짧은 URL입니다. 예를 들면go/calendar라는 URL이 있다면 바로 캘린더 페이지로 이동하게 됩니다. 이렇게 되면 자주 사용하는 URL에 바로 접속할 수 있어 문서 접근성이 향상됩니다.</p><p>콴다 팀도 이와 비슷하게 qanda.to라는 shorten URL을 이용해 문서와 서비스의 접근성을 높이고 있습니다. 예를 들어, qanda.to/readme로 가면 README 관련 대표 문서로 이동하게 됩니다. 이를 통해 개발자들이 원하는 정보에 손쉽게 접근하고, 문서의 신뢰도를 높이려고 노력하고 있습니다.</p><h4><strong>Fun Fun Fun!</strong></h4><p>업무가 단순히 반복되고 지루한 것이 되지 않도록 개발자들을 환기하는 것도 조직문화 레이드에서 중요한 일 중 하나라고 믿고 있습니다. 그래서 저희는 일주일 동안 개발자들이 기여한 금주의 PR 1등과 리뷰 1등을 선정하고 있습니다. 물론 이 지표가 성과평가나 누군가를 비난하기 위한 것은 아닙니다. 한 주 동안 열심히 보낸 사람들을 칭찬하고 함께 축하하기 위한 문화로 자리잡고 있습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/514/1*64l6VqBpmSyZa-gu70RKtQ.png" /></figure><h4><strong>콴다 테크 컨퍼런스 ‘리저브&#39; 개최!</strong></h4><p>2023년 동안 바쁘게 달려온 콴다 팀의 한 해를 축하하기 위해, 내부적으로 테크 컨퍼런스를 개최했습니다. 이는 단순히 지난 성과를 축하하는 것뿐만 아니라, 각자의 업무를 공유하고 다른 사람의 일을 이해하며 서로 시너지를 얻어 조직에서 성장할 수 있는 기회가 되기를 바라며 준비했습니다. 평소에는 조직이 세분화되고 각기 다른 엔지니어링 업무를 맡다 보니, 다른 팀이나 개발자가 무슨 일을 하는지 모를 때가 많습니다. 그런 장벽을 깨고 우리 모두가 공동의 목표를 위해 달려가고 있다는 것을 다시 한번 느끼는 기회가 되길 바랐습니다.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FAba0eFAdCS4%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DAba0eFAdCS4&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FAba0eFAdCS4%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/1021bfb28d37c393cd6a6dc203cda6e2/href">https://medium.com/media/1021bfb28d37c393cd6a6dc203cda6e2/href</a></iframe><p>‘QANDA RESERVE 2024’ 콴다 테크 컨퍼런스 현장 스케치에서는 각 엔지니어링 챕터에서 준비한 발표가 있었고, 연사님들이 다른 분야의 사람들도 잘 이해할 수 있도록 준비해 주셨습니다. 그 결과, 분야와 상관없이 정말 많은 질의응답이 오갔고, 열띤 컨퍼런스 현장을 모두가 체험할 수 있었습니다.</p><p>컨퍼런스 후기를 나누는 자리에서는, 다른 분야의 사람들이 어떤 일을 해왔는지 이해하면서 엔지니어로서 자극을 많이 받았다는 후기가 많았습니다. 또한, 콴다 팀이 추구하는 기술을 통한 교육적 혁신이라는 목표를 다시 한번 되새기는 계기가 되었습니다. 이처럼 테크 컨퍼런스는 단순한 축하를 넘어, 협업과 성장의 기회를 제공하며 콴다 팀의 엔지니어링 문화와 목표를 강화하는 중요한 행사로 기억되었습니다.</p><h4><strong><em>앞으로의 방향은?</em></strong></h4><p>이 조직문화 레이드의 궁극적인 목표는 모두가 같은 목표를 위해 함께 나아갈 수 있는 기틀을 마련하고, 엔지니어들이 성장할 수 있도록 다방면으로 지원하는 것이라고 생각합니다. 이 목표를 이루기 위해서는 여러 가지 노력과 문화적 전략이 필요할 것입니다. 예를 들어, 팀 간의 커뮤니케이션, 잘 정리된 엔지니어링 문서들, 그리고 팀별, 개인별 동기부여처럼 공동의 목표를 달성하는 데 도움이 되는 부분을 찾아가면서 발전시켜갈 계획입니다.</p><p>물론, 이러한 변화와 성과는 단기간에 이루어지기 어려울 수 있습니다. 하지만 작은 변화들이 모여 큰 변화를 만들어낼 것이라고 믿습니다. 구성원 각자가 자신의 역할을 충실히 수행하고, 긍정적인 조직 문화를 형성하는 데 기여한다면, 엔지니어링 조직문화는 결국 좋은 결과를 얻을 수 있을 것이고 이는 모두의 성장에 기여하게 될 것입니다.</p><p>이를 위해, 지속적인 피드백과 개선을 통해 우리 엔지니어링 조직의 문화가 점점 더 발전하고, 모든 구성원이 함께 성장할 수 있는 환경을 조성하는 콴다 팀의 엔지니어링 조직문화를 2024년에도 잘 가꿔나가겠습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5CrNalbHswdQEdfG4eEU7A.png" /></figure><p>🌏글로벌 Top 교육 앱 QANDA(콴다)를 함께 만들어 갈 엔지니어들을 기다리고 있습니다! ➡️ <a href="http://bit.ly/3pVjlmR">공고 확인하기</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=60df9136e050" width="1" height="1" alt=""><hr><p><a href="https://blog.mathpresso.com/%EC%BD%B4%EB%8B%A4-%ED%8C%80%EC%9D%98-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4%EB%A7%81-%EC%A1%B0%EC%A7%81%EB%AC%B8%ED%99%94-60df9136e050">콴다 팀의 엔지니어링 조직문화</a> was originally published in <a href="https://blog.mathpresso.com">Team QANDA</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Aurora/RDS 전문가의 GCP Cloud SQL이전기]]></title>
            <link>https://blog.mathpresso.com/aurora-mysql-rds-for-mysql-%EC%A0%84%EB%AC%B8%EA%B0%80%EC%9D%98-gcp-cloud-sql-for-mysql%EC%9D%B4%EC%A0%84%EA%B8%B0-%EC%9A%B0%EB%8B%B9%ED%83%95%ED%83%95-cloud-db-%EC%9D%B4%EC%A0%84%EA%B8%B0-27db5b17450d?source=rss----35e40785d286---4</link>
            <guid isPermaLink="false">https://medium.com/p/27db5b17450d</guid>
            <category><![CDATA[systemqa]]></category>
            <category><![CDATA[gcp]]></category>
            <category><![CDATA[sql]]></category>
            <dc:creator><![CDATA[Ian Lee]]></dc:creator>
            <pubDate>Mon, 20 May 2024 07:12:23 GMT</pubDate>
            <atom:updated>2024-05-20T07:21:22.035Z</atom:updated>
            <content:encoded><![CDATA[<h3>Aurora/RDS 전문가의 GCP Cloud SQL 이전기</h3><h4>우당탕탕 Cloud DB 이전기</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*LLcos_oQYAGcR1xozOj1lw.jpeg" /></figure><p>새로운 도약을 위한 모험은 항상 과제와 함께합니다. <br>저에게 Aurora MySQL/RDS for MySQL 전문가로서의 경험이 GCP라는 새로운 cloud platform 운영에 대한 도전으로 다시 다가왔습니다. 이번 글에서는 public cloud 간의 이전 과정을 “우당탕탕 Cloud DB 이전기”로 공유하여 public cloud 간 이전 혹은 on-premise와 public cloud 간 이전을 고민하는 개발자들에게 조금이나마 도움을 주고자 합니다.</p><h3>1. 목표</h3><p>AWS에서 GCP로의 크게 4가지 주요 DB 작업을 목표로 가지고 진행하게 했습니다.</p><ol><li>Aurora MySQL/RDS for MySQL/DynamoDB → GCP Cloud SQL for MySQL 이전</li><li>{1 DB : n Application} Architecture→ {1 DB: 1 Application} Architecture전환(DEV 환경)</li><li>service 안정성 향상과 Cost 절감을 위한 query, schema 및 index 최적화</li><li>MySQL version upgrade</li></ol><p>이 모든 작업은 service downtime(10분 이내) 및 이전 비용 최소화라는 기본 목표 위에 설정했습니다. 실제로는 2분 내외의 service downtime이 예상되었으나 초보 GCP 운영을 생각하여 속도 조절을 위한 buffer 시간을 추가했습니다.</p><p>특히 3번의 경우는 AWS에 선 적용 후 migration을 할 것인지 아니면 migration target(GCP)에만 적용할 것인지를 두고, 내부 논의를 하였고, 큰 작업에 대한 Aurora MySQL/RDS for MySQL의 temporary disk size 이슈와 현재의 query 를 검토하고 query, index 및 schema 변경에도 문제가 발생하지 않을 것이라는 자신감(?) 으로 후자를 선택하여 진행했습니다. 이 부분은 개발사마다의 환경(query 형태, DB spec, DB 구성 등)이 다를 수 있어 각 회사에 맞게 판단하시면 좋겠습니다.</p><h3>2. 사전 검토</h3><p>우리의 궁극적인 목표는 service downtime 및 이전 비용을 최소화하고, 이전 후에 장애가 없도록 하는 것이었습니다. 여기에, instance spec을 down scale하고, 개발자들의 투입 리소스를 최소화하는 것도 필요했습니다. 이를 위해 크게 5가지의 내용으로 검토했습니다.</p><p>검토 전에 먼저 DB의 현재 상황을 파악하기 위해 제일 먼저 DB 실시간 모니터링 환경(<em>grafana+victoriaMetrics+mysql/rds exporter</em>)을 구축한 후, 아래의 검토 사항을 진행했습니다.</p><h4>Step 1: 실행 query검토</h4><ul><li>service 실행 query를 분석하기 위해 general.log 를 이용해 실행 query를 추출하고 pt-query-digest 등등의 query generator를 통해 실행 query및 query 수행량 등을 파악합니다.</li></ul><h4>Step 2: schema 및 DB parameter 검토</h4><ul><li>주요 parameter 설정의 미설정 또는 오설정 여부 판단 및 schema type의 과설정 또는 오설정 판단 등등의 부하를 감소 시킬 수 있는 요소를 파악합니다.</li></ul><h4>Step 3: replica 구축 소요 시간 검토</h4><ul><li>6일 이내에 replication 구축이 완료되고 sync가 시작되어야 한다는 전제 조건을 만족해야 합니다.</li><li>사용 비용이 발생하는 solution(GCP Cloud Datastream / AWS DMS)을 사용하지 않기 위해 검토해야 합니다.</li></ul><ol><li>mysql export 시간 측정</li><li>dump file 전송시간 측정</li><li>mysql import 시간 측정</li><li>Sync complete 시간 측정</li></ol><h4>Step 4: version 차이에 따른 영향 검토</h4><ul><li>Aurora MySQL 2.x는 MySQL 5.7.x 기반이기 때문에 MySQL 8.0.2x 와의 parameter및 optimizer 차이에 따른 영향도를 검토해야 합니다.</li></ul><h4>Step 5: 사용 DB 종류의 단일화</h4><ul><li>Aurora MySQL/RDS for MySQL/DynamoDB → Cloud SQL for MySQL</li></ul><h3>3. 검토 결과</h3><h4>Step 1: 실행 query검토 결과</h4><ol><li>query 실행량이 너무 많음. 예를 들어 SELECT 1 과 같은 query는 단일 query로는 부하가 되지 않으나, 모이면 부하가 됩니다. 또, join은 부하가 크다라는 오해가 있어 join을 사용하지 않고 각각 실행하는 경우 오히려 더 부하가 됩니다.</li><li>single index가 많음. 이로 인해 scan량이 많고, 정렬이 늘어나게 됩니다.</li></ol><pre>e.g)<br>SELECT * FROM my_item <br>WHERE user_id=&#39;test&#39; <br>AND expired_time &gt; now() <br>ORDER BY expired_time;<br>의 index가<br><br>ALTER TABLE my_item ADD KEY ix_userid_expiredtime(user_id, expired_time);<br>가 아닌<br><br>ALTER TABLE my_item ADD KEY ix_userid(user_id);<br>ALTER TABLE my_item ADD KEY ix_expiredtime(expired_time);<br>로 생성되어 있다.</pre><p>특히 admin site의 filter 조건 대응을 위해 column별 data 분포도와는 상관없이 모든 filter 항목별로 각각 single index를 생성한 경우도 있어 이 부분에 대한 검토가 필요했습니다.</p><p>3. or query가 많음. 아래 예제 query에서는 expired_time에 대해 NULL 대신 default값(ex.9999–12–31)을 주어지면 or를 제거할 수 있습니다. 제거하지 않는다면 optimizer에 따라 expired_time index를 활용할 수 없게 됩니다.</p><pre>e.g)<br>SELECT * <br>FROM my_item <br>WHERE id = 1 <br>AND (expired_time &gt; now() OR expired_time IS NULL); </pre><p>4. order by single-index-column desc limit xx query로 인한 오동작 수행 query가 다수 존재함.(<a href="https://percona.community/blog/2019/07/29/mysql-optimizer-naughty-aberrations-on-queries-combining-where-order-by-and-limit/">MySQL Optimizer: Naughty Aberrations on Queries Combining WHERE, ORDER BY and LIMIT</a>). 잘못된 index 활용으로 인해 full scan 급의 data scan이 실행되어 큰 부하가 발생합니다.</p><p>5. transaction 이 많음. transaction이 필요한 DML(insert,update,delete) query가 아닌 select query만 실행되는 부분에도 begin 또는 auto_commit=0을 실행합니다. 이로 인해 commit이 많습니다. commit이 많으면 부하가 올라갑니다.</p><p>6. 건수가 적은 table에 대해서는 index 없이 전체 full scan query가 많음.작은 고추가 맵다고 건수가 적어도 실행량이 많으면 문제가 됩니다.</p><p>7. admin page들의 검색 query에 like ‘%명칭%’ 검색이 많음. 건수가 많지 않고 잦은 실행이 아니라면 무시할 수 있지만, 그 반대라면 full text search engine을 사용하는 것이 좋습니다.</p><p>8. join query 형태로 구현할 수 있는 query가 subquery 형태로 구현된 경우가 많음. optimizer 판단에 따라 subquery부터 실행되지 않을 수 있어 의도와는 다르게 부하를 유발하는 query가 될 수 있습니다.</p><p>9. in 절의 값들이 너무나 과도하게 들어 있음. in 값의 개수가 너무 과도한 경우 full scan으로 실행되는 경우가 있어 주의가 필요합니다. 이유를 여기서 설명하면 너무 길어지기 때문에 이유가 궁금하신 분들은 <a href="https://medium.com/daangn/index-dive-%EB%B9%84%EC%9A%A9-%EC%B5%9C%EC%A0%81%ED%99%94-1a50478f7df8">Index Dive</a> 를 참고하면 좋습니다.</p><p>10. 현재 version의 Cloud SQL for MySQL은 이슈가 해소되었지만, 콴다팀의 migration시에는 <a href="https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_lower_case_table_names">lower_case_table_names</a> 옵션이 제공되지 않아 query 에서 table명 대소문자 사용과 table 생성의 table명 대소문자 여부를 비교 확인했어야 했습니다.</p><h4>Step 2: schema 및 DB parameter 검토 결과</h4><ol><li>과도한 longtext type 사용. text type과 varchar의 큰 size는 모두 longtext type으로 사용했습니다. 심지어 flag성 column의 경우도 longtext를 사용했습니다.</li><li>auto increment id 발행 수 초과 이슈 존재. auto increment 한계 수치에 도달했거나, 거의 도달하고 있는 int pk column 이 존재했습니다.</li><li>varchar size의 과도 설정. 특히 flag성 column size가 255로 설정되어 있는 경우가 많았습니다.</li><li>sql_mode 미적용 상태. sql_mode 가 null로 적용되어 있어, DB 입장에서는 warning 이지만, biz. 입장에서는 오류인 상황이 발생했습니다.</li><li>replication 설정이 되어 있지 않음. replication 설정을 위해서는 binlog_formet의 off 값을 바꿔야 했습니다.</li><li>table당 index가 과다 생성됨. performance schema 가 사용 가능한 spec이라면 아래의 query를 이용하여 미사용 index를 찾았습니다.(performance schema data는 휘발성이기 때문에 오랜시간 restart등을 하지 않은 상태일수록 정확할 것입니다.) performance schema를 사용할 수 없는 경우는 general log를 이용해 sql을 남겨 index 사용여부를 개발자와 논의하여 결정했습니다.</li></ol><pre>SELECT  OBJECT_SCHEMA, OBJECT_NAME, INDEX_NAME     <br>FROM performance_schema.table_io_waits_summary_by_index_usage <br>WHERE INDEX_NAME IS NOT NULL<br>AND OBJECT_SCHEMA not in (&#39;performance_schema&#39;,&#39;mysql&#39;,&#39;information_schema&#39;,&#39;sys&#39;) <br>AND OBJECT_TYPE=&#39;TABLE&#39;<br>AND COUNT_STAR = 0<br>ORDER BY 1,2;</pre><h4>Step 3: replica 구축 소요 시간 검토 결과</h4><p>6일 이내에 replication 구축이 완료되고 sync가 시작되어야 한다는 전제 조건을 만족해야 합니다.</p><ol><li>mysql export 시간 측정</li></ol><p>table이 많은 경우 table들을 parallel로 import하면 많은 시간을 줄일 수 있기 때문에 아래의 query를 이용하여 table별 file size를 추출, 전체 평균 size에 맞추며 parallel로 export를 실행했습니다.</p><pre>SELECT file_name, ROUND(SUM(total_extents * extent_size)/1024/1024/1024,2) AS &quot;TableSizeinGB&quot; <br>FROM information_schema.files <br>WHERE file_name like &#39;%table명%&#39; group by total_extents * extent_size;</pre><pre>mysqldump -uwriter -p -htest.com --databases test --tables t1 t2 --skip-lock-tables --complete-insert --opt --hex-blob --routines --triggers --skip-add-locks --default-character-set=utf8mb4 --no-create-info --no-create-db --skip-column-statistics --set-gtid-purged=OFF &gt; schema.sql</pre><p>2. dump file 전송시간 측정</p><pre>server : <br>nc -l 9999 | tar xvfz -<br>client :<br>tar cvfz - filename | nc mysql.test.ai 9999</pre><p>물론 mysqldump와 netcat을 pipeline을 이용하여 아래와 같이 1,2번을 한 번에 실행 할 수도 있습니다.</p><pre>client :<br>mysqldump -uwriter -p -htest.com --databases test --skip-lock-tables --complete-insert --opt --hex-blob --routines --triggers --skip-add-locks --default-character-set=utf8mb4 --no-create-info --no-create-db --skip-column-statistics --set-gtid-purged=OFF | tar cvzf - | nc mysql.test.ai 9999</pre><p>3. mysql import 시간 측정</p><p>table size별로 export 받은 File을 disk IO를 고려하여 parallel로 import하면 많은 시간을 줄일 수 있습니다.<br>이를 위해 table별 size를 이용하여 큰 것과 작은 것 짝을 맞춰 parallel 하게 실행합니다.</p><p>만일, 단일 table의 data size가 매우 큰 경우는 아래와 같이 line number를 이용하여 마치 oracle의 pump와 비슷한 형태로 parallel import를 할 수도 있겠습니다.</p><pre> grep &#39;^INSERT INTO&#39; data.sql | awk &#39;(NR % parallel수)==1&#39; |./mysql -h&#39;target-mysql.test.ai&#39; --init-command=&quot;SET FOREIGN_KEY_CHECKS=0;&quot; --database=target_db -utest -p&#39;test&#39;&gt; /dev/null 2&gt; error1.log</pre><p>4. Sync complete 시간 측정</p><p>innodb_flush_log_at_trx_commit이 public cloud DB의 기본 설정인 1인 경우 DML traffic이 많은 service spec을 service 기준보다 높이지 않는 경우 결코 Sync가 완료되지 못했습니다. spec높이고 sync 완료시키고 service open전에 spec down을 하는 방식으로 하면 spec down 시간만큼 소요 시간이 증가하게 됩니다. 따라서 migration 시간을 줄이기 위해서는 꼭 innodb_flush_log_at_trx_commit을 0,2로 바꿀 수 있어야 하겠습니다.</p><h4>Step 4: version 차이에 따른 영향 검토 결과</h4><ul><li>다양한 default 설정값이 변하였으나, 회사 내부 기준에 따라 변경 대상이 되는 아래의 variables 에는 critical한 영향이 없는 것으로 파악됐습니다.</li></ul><ol><li>default character</li><li>sql_mode</li><li>default_authentication_plugin</li><li>optimizer_switch</li><li>time_zone</li></ol><h4>Step 5: 사용 DB 종류의 단일화 검토 결과</h4><ul><li>Aurora MySQL/RDS for MySQL → Cloud SQL for MySQL<br> variables 설정 차이(version 설정 차이)로부터의 issue 가 예상되는 것은 없었습니다.</li><li>DynamoDB → Cloud SQL for MySQL<br>복제 구성을 위한 data 이전은 data copy application 개발로 대응하였습니다. DMS 사용을 고려할 수도 있었으나, 비용이 발생하고 내부적으로 데이터 압축을 통한 이전으로 결정되어 data copy application으로 data 이전을 하기로 했습니다.<br>DynamoDB → Cloud SQL for MySQL 의 이전은 Test 결과 response 성능이 보다 안정적으로 제공되고, 특히 비용이 약 1/8 정도로 절감되는 매우 큰 장점이 있습니다.</li></ul><pre>이러한 5step의 검토로 부터 구체적 작업시간을 공개할 수는 없지만, <br>AWS의 최대 binlog 보관 기간인 7일이내에 migration 작업이 충분히 가능한 것으로 결과가 도출되어<br>예상 senario대로 migration비용을 최소화하며 migration을 진행했습니다.</pre><h3>4. migration 선행 작업</h3><h4>일괄 작업 script 혹은 App. 개발</h4><p>DB cluster가 많은 환경에서 migration 시간을 단축하려는 방법으로 모든 cluster가 같은 작업에 대해 동시에 작업될 수 있도록 하는 것도 중요합니다.</p><ol><li>binlog format 변경</li><li>binlog retention 기간 변경</li><li>instance demote</li><li>writer read_only = on 으로 변경</li><li>instance promote</li><li>session kill</li><li>service domain IP 변경 — writer,reader,admin</li><li>reader 추가</li></ol><h4>Cloud SQL for MySQL migration Senario 작성</h4><p>migration Senario를 작성하여 타부서와 작업 내용을 공유함으로써 역할을 분배하고 migration 작업의 허점은 없는 지 Cross Check하는 것도 중요합니다.</p><ol><li>DB 접속용 service domain을 생성하여 이전 전에 service domain으로 service가 될 수 있도록 app. server 배포</li><li>service 점검 시작</li><li>Aurora MySQL/RDS for MySQL writer instance를 read_only로 변경</li><li>Cloud Sql for MySQL Reader instance promote</li><li>모든 service domain(writer/reader/admin)에 promote된 instance ip 추가 후, 구 writer instance ip 제거</li><li>구 writer instance 의 session 정리(미리 작성해 둔 script를 이용하여 일괄 정리)</li><li>구 writer instance stop</li><li>service open</li><li>Cloud Sql for MySQL reader 생성</li><li>admin domain에 reader instance ip 추가 후, writer instance ip 제거</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*EgMYtjHNekHyWqq87b3XGA.gif" /><figcaption>migration process</figcaption></figure><h3>5. migration 실행 준비 작업</h3><p>기존 DEV은 {1 DB: n Application} Architecture 형태로 되어 있고, PROD 환경은 {1 DB: 1 Application} Architecture 형태로 되어 있는 등 상이하여 같은 {1 DB: 1 Application}로의 이전을 하기 위해서는 DEV 환경의 replication이 조금은 특이하게 구성해야만 합니다.</p><p>왜냐하면 public cloud 환경에서는 replication 구성시 replication filter(replicate-do-db,replicate-ignore-db) 옵션 사용이 불가이기 때문입니다.</p><p>물론 전 DB를 모두 export 후 import하여 replication을 구성하고 향후 관련없는 database를 Drop하는 방법도 있겠지만, 이렇게 되면 {1 DB: 1 Application Architecture} 구성 이후 작은 규모의 DB instance들의 Disk Size들도 기존 통합DB size만큼 커지게 되어 불필요한 비용이 지속적으로 청구되기 때문에 이 방법은 사용할 수 없습니다.</p><p>RDS와 마찬가지로 Cloud SQL의 Disk Size도 증가는 online 중에 가능하지만, 감소는 불가능합니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0xDzMnazvmlfZYt3BocoMA.png" /><figcaption>migration process</figcaption></figure><h4>Step 1: writer binlog format 적용</h4><ul><li>[DEV] binlog_format = <strong>STATEMEMT</strong><br><em>empty table에 대해 delete,update 구문 실행시 error를 발생시키지 않게 하기 위함입니다. error가 발생하는 경우는 replication이 멈추게 되기 때문입니다.</em></li><li>[PROD] binlog_format = <strong>ROW</strong></li></ul><h4>Step 2: Aurora MySQL/RDS for MySQL의 binlog retention 기간 변경</h4><ul><li>Aurora MySQL/RDS for MySQL의 binlog retention 기간을 7일로 변경하고, mysql replication 을 이용하여 failover 방식으로 이전을 진행합니다.</li></ul><pre>-- 7 * 24 = 168<br>call mysql.rds_set_configuration(&#39;binlog retention hours&#39;, 168);<br>show binary logs;</pre><h4>Step 3: DB Connection URI를 service domain으로 변경</h4><ul><li>Graceful migration을 위해 DB Connection 정보 변경을 중앙에서 보다 쉽게 할 수 있도록 해 점검시간 중의 app. server 재배포로 인한 소요시간을 없앱니다.</li></ul><h4>Step 4: Data export 대상 instance 구축</h4><ul><li><strong>Aurora MySQL</strong><br>snapshot backup 이후 복구하고, Recent events로부터 crash recovery position을 기록해 둡니다.<br>복구한 instance로부터 export를 진행합니다. 이 때 반드시 해당 database/table의 characterset을 맞추어(mysqldump 옵션에 — default-character-set=xxxxx 추가 ) export를 해야 character가 깨지는 상황을 방지할 수 있습니다.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*N2_cgHw-k3Erl4NLMc89eQ.jpeg" /></figure><ul><li><strong>RDS for MySQL<br></strong>RDS for MySQL은 Aurora MySQL과 달리 snapshot backup events에 binlog position 정보가 남지 않습니다. 따라서, on-premise와 같이 reader를 추가한 이후 추가 reader를 replication stop하고 show replica status(구 show slave status)를 통해 binlog position을 기록해 둡니다.<br>신규로 추가한 instance로부터 export를 진행합니다.</li></ul><pre>CALL mysql.rds_stop_replication;</pre><pre>mysql&gt; SHOW REPLICA STATUS\G<br>*************************** 1. row ***************************<br>             Replica_IO_State: Waiting for source to send event<br>                  Source_Host: mysql.test.ai<br>                  Source_User: writer<br>                  Source_Port: 13000<br>                Connect_Retry: 1<br>              Source_Log_File: master-bin.000001<br>          Read_Source_Log_Pos: 927<br>               Relay_Log_File: slave-relay-bin.000002<br>                Relay_Log_Pos: 1145<br>        Relay_Source_Log_File: master-bin.000001<br>           Replica_IO_Running: No<br>          Replica_SQL_Running: No<br>              Replicate_Do_DB:<br>          Replicate_Ignore_DB:<br>           Replicate_Do_Table:<br>       Replicate_Ignore_Table:<br>      Replicate_Wild_Do_Table:<br>  Replicate_Wild_Ignore_Table:<br>                   Last_Errno: 0<br>                   Last_Error:<br>                 Skip_Counter: 0<br>          Exec_Source_Log_Pos: 927<br>              Relay_Log_Space: 1355<br>              Until_Condition: None<br>               Until_Log_File:<br>                Until_Log_Pos: 0<br>           Source_SSL_Allowed: No<br>           Source_SSL_CA_File:<br>           Source_SSL_CA_Path:<br>              Source_SSL_Cert:<br>            Source_SSL_Cipher:<br>               Source_SSL_Key:<br>        Seconds_Behind_Source: 0<br>Source_SSL_Verify_Server_Cert: No<br>                Last_IO_Errno: 0<br>                Last_IO_Error:<br>               Last_SQL_Errno: 0<br>               Last_SQL_Error:<br>  Replicate_Ignore_Server_Ids:<br>             Source_Server_Id: 1<br>                  Source_UUID: 73f86016-978b-11ee-ade5-8d2a2a562feb<br>             Source_Info_File: mysql.slave_master_info<br>                    SQL_Delay: 0<br>          SQL_Remaining_Delay: NULL<br>    Replica_SQL_Running_State: Replica has read all relay log; waiting for more updates<br>           Source_Retry_Count: 10<br>                  Source_Bind:<br>      Last_IO_Error_Timestamp:<br>     Last_SQL_Error_Timestamp:<br>               Source_SSL_Crl:<br>           Source_SSL_Crlpath:<br>           Retrieved_Gtid_Set: 73f86016-978b-11ee-ade5-8d2a2a562feb:1-3<br>            Executed_Gtid_Set: 73f86016-978b-11ee-ade5-8d2a2a562feb:1-3<br>                Auto_Position: 1<br>         Replicate_Rewrite_DB:<br>                 Channel_Name:<br>           Source_TLS_Version:<br>       Source_public_key_path:<br>        Get_Source_public_key: 0<br>            Network_Namespace:</pre><h4>Step 5: Aurora MySQL/RDS for MySQL→ Cloud SQL for MySQL 간 replica 구축</h4><p>export는 두 가지 형태로 합니다. 하나는 전체 schema export, 또 다른 하나는 복제 대상 database의 data만 export를 합니다.</p><ol><li>schema export 의 내용을 수정:</li></ol><ul><li>적절치 않은 longtext type 제거</li><li>A.I. pk int-&gt;bigint</li><li>varchar size 감소</li><li>enum type 제거</li><li>불필요 index 제거</li><li>index 생성 구문 분리(schema_table.sql, schema_index.sql) <br>→ data적재시 index가 없는 상태에서 적재하고, 추후 alter 구문하나로 생성(특히 index개수가 3개/table 이상의 경우는 더욱) 하는 것이 data scan을 한번만 하므로 속도가 빠릅니다.<br>(e.g. <em>ALTER TABLE T ADD KEY idx_1(1),ADD KEY idx_2(2),ADD KEY idx_3(3),ADD KEY idx_4(4));</em>)</li><li>schema 내의 characterSet, collection 제거<br>등등</li></ul><p>2. 데이터 초기 적재:</p><ul><li>remote replica 생성을 위한 Cloud SQL for MySQL 을 생성합니다.<br>기본 characterSet은 utf8mb4 생성했으며, 이 set은 super Set이기때문에 import로 인한 깨짐 현상은 발생하지 않습니다.</li><li>schema_table.sql 을 실행합니다.</li><li><strong><em>replica 구축 소요시간 검토</em> 의 </strong>1. mysql export 시간 측정, 2. dump file 전송시간 측정에서 언급한 방식으로 Bastion instance까지 export_data.sql을 전송합니다.</li><li>data 이전과 동시에 Bastion instance에서 전송 받을 export_data.sql에 대해 import를 병렬로 실행하고, import data가 완료되면 schema_index.sql을 실행합니다.</li><li>replication 구성이후(demote이후)에는 target instance는 super_read_only mode로 변경되어 schema 및 data 작업이 불가하므로 반드시 replication 구성 전에 DB 계정 생성까지 모두 완료하고 demote를 해야 합니다.</li></ul><p>3. replication 구성:</p><ul><li>GCP의 경우 AWS나 on-premise와 같이 source writer에 직접적인 replication 연결이 불가능합니다.</li><li>External meta virtual instance를 생성하여 replication writer instance 역할을 하도록 설정합니다.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/445/1*669iasd_GEB-7_sDpRLTHw.png" /></figure><pre>gcloud auth login<br>ACCESS_TOKEN=&quot;$(gcloud auth print-access-token)”<br>curl --header &quot;Authorization: Bearer ${ACCESS_TOKEN}&quot; --header &#39;Content-Type: application/json&#39; --data @./source.json -X POST https://sqladmin.googleapis.com/sql/v1beta4/projects/project명/instances</pre><pre>source.json<br>{<br>      &quot;name&quot;: &quot;SOURCE_NAME&quot;,<br>      &quot;region&quot;: &quot;REGION&quot;,<br>      &quot;databaseVersion&quot;: &quot;DATABASE_VERSION&quot;,<br>      &quot;onPremisesConfiguration&quot;: {<br>        &quot;hostPort&quot;: &quot;SOURCE_HOST&quot;,<br>        &quot;username&quot;: &quot;USERNAME&quot;,<br>        &quot;password&quot;: &quot;PASSWORD&quot;,<br>        &quot;caCertificate&quot;: &quot;SOURCE_CERT&quot;,<br>        &quot;clientCertificate&quot;: &quot;CLIENT_CERT&quot;,<br>        &quot;clientKey&quot;: &quot;CLIENT_KEY&quot;<br>      }<br>}</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*n999AwlxmCVfjq_pCyPm-Q.png" /></figure><h4>Step 6: instance demote</h4><p>Cloud SQL 의 demote는 console 에서 지원되지 않기 때문에 sdk 또는 API를 이용하여 대응해야 합니다. 아래는 demote 를 위한 golang 예제입니다.</p><ul><li>masterName 은 representation instance의 NAME 입니다.</li><li>SkipReplicationSetup를 true 하지 않으면 demote와 동시에 replication 연결을 시도하므로, 반드시 false로 설정해야 합니다.</li></ul><pre>func SetDemoteMaster(ctx context.Context, sqladminService *sqladmin.Service, gcpProjectId, masterName, instanceName string) error {<br> var demoteMasterContext = new(sqladmin.DemoteMasterContext)<br><br> demoteMasterContext = &amp;sqladmin.DemoteMasterContext{<br>  // # Database instance demote master context. # Contains details about the demoteMaster operation.<br>  //  Kind:                  &quot;sql#demoteMasterContext&quot;,      //# This is always sql#demoteMasterContext.<br>  MasterInstanceName:    masterName, //# The name of the instance which will act as on-premises master in the replication setup.<br>  VerifyGtidConsistency: false,          //# Verify GTID consistency for demote operation. Default value: True. Second Generation instances only. Setting this flag to false enables you to bypass GTID consistency check between on-premises master and Cloud SQL instance during the demotion operation but also exposes you to the risk of future replication failures. Change the value only if you know the reason for the GTID divergence and are confident that doing so will not cause any replication issues.<br>  SkipReplicationSetup:  true,<br>  ReplicaConfiguration: &amp;sqladmin.DemoteMasterConfiguration{<br>   // # Read-replica configuration for connecting to the on-premises master.<br>   // # Configuration specific to read-replicas replicating from the on-premises master.<br>   //   Kind: &quot;sql#demoteMasterConfiguration&quot;, //# This is always sql#demoteMasterConfiguration.<br>   MysqlReplicaConfiguration: &amp;sqladmin.DemoteMasterMySqlReplicaConfiguration{<br>    //# Read-replica configuration specific to MySQL databases.<br>    //# MySQL specific configuration when replicating from a MySQL on-premises master.<br>    //  Replication configuration information such as the username, password, certificates, and keys are not stored in the instance metadata.<br>    //  The configuration information is used only to set up the replication connection and is stored by MySQL in a file named master.info in the data directory.<br>    Username: replication계정명, //# The username for the replication connection.<br>    //    Kind:     &quot;sql#demoteMasterMysqlReplicaConfiguration&quot;, //# This is always sql#demoteMasterMysqlReplicaConfiguration.<br>    //    ClientKey:         &quot;A String&quot;,                                  //# PEM representation of the slave&#39;s private key. The corresponsing public key is encoded in the client&#39;s certificate. The format of the slave&#39;s private key can be either PKCS #1 or PKCS #8.<br>    //    CaCertificate:     &quot;A String&quot;,                                  //# PEM representation of the trusted CA&#39;s x509 certificate.<br>    //    ClientCertificate: &quot;A String&quot;,                                  //# PEM representation of the slave&#39;s x509 certificate.<br>    Password: replication계정 비밀번호, //# The password for the replication connection.<br>   },<br>  },<br>  ForceSendFields: []string{&quot;VerifyGtidConsistency&quot;, &quot;SkipReplicationSetup&quot;},<br> }<br><br> rb := &amp;sqladmin.InstancesDemoteMasterRequest{<br>  DemoteMasterContext: demoteMasterContext,<br> }<br><br> resp, err := sqladminService.Instances.DemoteMaster(gcpProjectId, instanceName, rb).Context(ctx).Do()<br> if err != nil {<br>  log.ERROR().Errorf(&quot;Failed to Demote Master  (%s, %s) : %+v&quot;, gcpProjectId, instanceName, err)<br>  return err<br> }<br><br> err = InstanceOperationCheck(ctx, sqladminService, gcpProjectId, resp, 1, nil)<br> if err != nil {<br>  log.ERROR().Errorf(&quot;Failed to Check for Demote Master : %+v&quot;, err)<br>  return err<br> }<br><br> return nil<br>}</pre><h4>Step 7: replication 성능 및 column type 변환 대응을 위한 옵션 적용</h4><ul><li>innodb_flush_log_at_trx_commit의 설정값을 2로 변경해야 합니다.</li><li>replica_type_conversions 의 설정값을 ALL_LOSSY,ALL_NON_LOSSY 로 변경합니다. 설정값을 변경하지 않으면 binlog_format=ROW에서 writer와 reader의 column type이 다른 경우 replication error가 발생합니다.</li></ul><h4>Step 8: replication Start</h4><pre>call mysql.resetMaster();<br>call mysql.setupExternalSource(&#39;Represetation_Instance_Name&#39;, 3306, &#39;rep&#39;, &#39;rep&#39;, &#39;mysql-bin-changelog.000002&#39;, 2170, false, false);<br>call mysql.startReplication();<br>show slave status\G;</pre><h3>6. migration 실행</h3><h4>Aurora MySQL/RDS for MySQL→ GCP Cloud SQL for MySQL 데이터 이전</h4><p>선행 작업 단계에서 정리한 내용으로 진행합니다.</p><ol><li>service 점검 시작</li><li>Aurora MySQL/RDS for MySQL writer instance를 read_only로 변경</li><li>Cloud SQL for MySQL reader instance promote</li><li>모든 service domain(writer/reader/admin)에 promote된 instance ip 추가 후, 구 writer instance ip 제거</li><li>구 writer instance 의 session 정리</li><li>instance가 promote되면 innodb_flush_log_at_trx_commit=1 로 변경됩니다. innodb_flush_log_at_trx_commit는 replica에 한해서 Console상에서 설정이 2로의 변경이 지원됩니다. 다만, 이 옵션을 변경하여 지속적으로 운영하는 경우 SLA를 보장 받을 수 없게 됩니다.</li><li>구 writer instance stop</li><li>service open</li><li>Cloud SQL for MySQL reader 생성</li><li>admin domain에 reader instance ip 추가 후, writer instance ip 제거</li><li><strong><em>구 writer instance terminate</em></strong></li><li><strong><em>representation instance terminate</em></strong></li></ol><h3>7. 장애</h3><ul><li>sql_mode = STRICT_TRANS_TABLES 적용으로 <br>varchar size를 넘는 데이터를 저장할 때 warning이 발생하던 것이 error로 발생한 것 뿐이였습니다. 이것은 error가 발생되어 error 수정을 하는 것이 맞는 것으로 정상적인 error입니다.</li><li><a href="https://percona.community/blog/2019/07/29/mysql-optimizer-naughty-aberrations-on-queries-combining-where-order-by-and-limit/">MySQL Optimizer: Naughty Aberrations on Queries Combining WHERE, ORDER BY and LIMIT</a> 이슈를 해결하고자 일부 DB에 아래의 옵션에 대해 값을 변경 적용했지만,</li></ul><pre>set global optimizer_switch=`prefer_ordering_index=off`;</pre><p>다음과 같은 query가 full scan으로 실행됨에 따라 부하가 상승하여 <br>(벼룩 잡으려다 초가삼간 다 태웠습니다. 😭)</p><pre>SELECT * FROM my_items ORDER BY id DESC limit 10;</pre><p>on 으로 Rollback하고 다음과 같은 형태로 문제의 query에 hint로 적용하여 문제대응을 하였습니다.</p><pre>SELECT /*+ SET_VAR(optimizer_switch = &#39;prefer_ordering_index=off&#39;) */ `test`.`id`,...</pre><h3>마치며</h3><p>이렇게 구체적인 검토/준비/실행 단계를 거쳐, 콴다팀에서는 Aurora MySQL/RDS for MySQL에서 GCP Cloud SQL for MySQL로의 DB이전을 목표시간내에 성공적으로 완료하였고, service를 더욱더 안정적으로 운영할 수 있게 되었습니다.<br>위의 process는 migration 뿐만 아니라 MySQL version upgrade시에도 같은 방법으로 진행할 수 있습니다. 개발자분들께 많은 도움이 되는 내용이길 바랍니다.</p><h3>추가로</h3><p>migration이후의 Cloud SQL for MySQL 사용에서 느낀 장단점을 간단히 언급해 보면</p><ul><li>장점 : <br>1) query insight 가 있어 지정된 기간동안 실행된 거의 모든 query를 확인할 수 있어 부하 발생시 query분석을 general.log를 통한 query generation 을 하지 않고도 어렵지 않게 살펴볼 수 있어 개발자들에게 많은 도움이 됩니다. AWS performance insights가 있습니다만, 제한적으로 보여주고 있는데, CPU 부하기준으로 top 25만을 뽑기때문에 부하는 크지 않지만, 실행이 많아 부하가 되는 query등등 전체 instance 관점에서의 확인하기 어렵습니다.<br>2) AWS의 spec이 static 한 것에 비해 Core 수에 따라 memory size를 특정 범위내에서 자유롭게 지정이 가능합니다. memory size를 꼭 2^n으로 하지 않아도 된다네요. 물론 Core수도 다양하게 지정이 가능합니다.</li><li>단점 : <br>1) on-premise/RDS와 마찬가지로 대용량 table의 DDL작업으로 인한 replication delay 가 발생합니다. <br>물론 최근 MySql 8.0 version의 add column의 instant algorithm이 도입되어 replication delay issue에서 어느 정도 벗어났지만, 여전히 이외의 DDL문에서는 replication delay 현상이 존재하기 때문에 이를 방지를 위해서는 3rd party 솔루션으로 작업해야 합니다. <br>하지만, 널리 알려진 pt-online-schema-change 는 id를 명시적 A.I. pk로 사용하고, 논리 pk를 unique key를 사용하는 콴다팀 구성의 경우 trigger 복제를 이용한 작업의 경우 dead lock이 발생할 가능성이 높아 콴다팀에서는 <a href="https://github.com/github/gh-ost">gh-ost</a> 를 사용하고 있습니다.<br>2) RDS와 같이 HA 구성을 위해서는 reader와는 별개의 추가 instance 비용이 발생하며, 이 instance는 active로의 활용이 불가합니다.<br>3) Cluster 간 replication 구성이 직관적이지 못 합니다.</li></ul><p>양 public cloud DB의 성능 차이는 구체적 system spec 및 BMT 방법, traffic의 성격에 따라 상이할 수 있기 때문에 구체적으로 BMT를 진행하지 않았으며, 결과적으로는 AWS 에 비해 비용절감이 되었습니다.</p><p>🌏글로벌 Top 교육 앱 QANDA(콴다)를 함께 만들어 갈 엔지니어들을 기다리고 있습니다! ➡️ <a href="http://bit.ly/3pVjlmR">공고 확인하기</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=27db5b17450d" width="1" height="1" alt=""><hr><p><a href="https://blog.mathpresso.com/aurora-mysql-rds-for-mysql-%EC%A0%84%EB%AC%B8%EA%B0%80%EC%9D%98-gcp-cloud-sql-for-mysql%EC%9D%B4%EC%A0%84%EA%B8%B0-%EC%9A%B0%EB%8B%B9%ED%83%95%ED%83%95-cloud-db-%EC%9D%B4%EC%A0%84%EA%B8%B0-27db5b17450d">Aurora/RDS 전문가의 GCP Cloud SQL이전기</a> was originally published in <a href="https://blog.mathpresso.com">Team QANDA</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[QANDAが 第14回 EDIX東京へ！ご来場ありがとうございました！]]></title>
            <link>https://blog.mathpresso.com/qanda%E3%81%8C-%E7%AC%AC14%E5%9B%9E-edix%E6%9D%B1%E4%BA%AC%E3%81%B8-%E3%81%94%E6%9D%A5%E5%A0%B4%E3%81%82%E3%82%8A%E3%81%8C%E3%81%A8%E3%81%86%E3%81%94%E3%81%96%E3%81%84%E3%81%BE%E3%81%97%E3%81%9F-0dc514aca3a1?source=rss----35e40785d286---4</link>
            <guid isPermaLink="false">https://medium.com/p/0dc514aca3a1</guid>
            <category><![CDATA[insights]]></category>
            <category><![CDATA[q-and-a]]></category>
            <dc:creator><![CDATA[Jason Jung]]></dc:creator>
            <pubDate>Sat, 11 May 2024 09:48:31 GMT</pubDate>
            <atom:updated>2024-05-11T09:48:31.392Z</atom:updated>
            <content:encoded><![CDATA[<p>アジア代表AI教育アプリのQANDAが第14回 EDIX参加のため東京を訪問しました!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*-X8sC_7IZX9hO-8DfJSsQQ.jpeg" /></figure><p>大変多くの方々に弊社ブースにお立ち寄りいただき盛況のうちに展示会を執り行うことができました。日本のIT大企業、教育会社、院・学校の関係者様、以外にQANDAに関心を持っていただき、訪問してくださった皆様に感謝申し上げます。</p><p>頂戴いたしました様々なご意見・ご要望を生かし、今からみなさまのご期待に沿えますようサービス開発に努めてまいります。</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*upQlcUF0pfNqG3s7i4VQeA.jpeg" /></figure><p>QANDAは、日本国内のさまざまなビジネス パートナーシップに対して開かれています。 サービスに関するご質問、またはビジネスパートナーシップに関するお問い合わせは、taka.hirano@mathpresso.com までお願いいたします。</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=0dc514aca3a1" width="1" height="1" alt=""><hr><p><a href="https://blog.mathpresso.com/qanda%E3%81%8C-%E7%AC%AC14%E5%9B%9E-edix%E6%9D%B1%E4%BA%AC%E3%81%B8-%E3%81%94%E6%9D%A5%E5%A0%B4%E3%81%82%E3%82%8A%E3%81%8C%E3%81%A8%E3%81%86%E3%81%94%E3%81%96%E3%81%84%E3%81%BE%E3%81%97%E3%81%9F-0dc514aca3a1">QANDAが 第14回 EDIX東京へ！ご来場ありがとうございました！</a> was originally published in <a href="https://blog.mathpresso.com">Team QANDA</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[AI Learning Apps and Academic Confidence: Insights from a National Survey]]></title>
            <link>https://blog.mathpresso.com/the-connection-between-ai-powered-learning-app-usage-and-academic-confidence-a-national-survey-9911d1efaf73?source=rss----35e40785d286---4</link>
            <guid isPermaLink="false">https://medium.com/p/9911d1efaf73</guid>
            <category><![CDATA[academic-performance]]></category>
            <category><![CDATA[ai-powered-education]]></category>
            <category><![CDATA[insights]]></category>
            <dc:creator><![CDATA[Sang-Pil Han]]></dc:creator>
            <pubDate>Thu, 25 Apr 2024 02:25:58 GMT</pubDate>
            <atom:updated>2024-04-25T04:33:36.846Z</atom:updated>
            <content:encoded><![CDATA[<h4><strong>Exploring the Impact of AI-Powered Learning Apps on Students’ Performance</strong></h4><p>In an era where digital education tools are essential, understanding the influence of AI-powered learning apps on student outcomes has never been more crucial. Our team took advantage of the shift towards virtual classrooms during the COVID-19 pandemic to study this important dynamic.</p><p><strong>Our Survey Insights: Assessing QANDA’s Role in Educational Achievement</strong></p><p>We conducted a nationwide survey over a four-week period at the end of 2020, gathering responses from 2,145 users of the QANDA app in South Korea. Our aim was to correlate app usage with improvements in students’ academic performance in math and to evaluate psychological and emotional benefits.</p><p><strong>Linking App Usage to Academic and Personal Growth</strong></p><p>Our survey results indicated a significant trend: students with frequent usage of QANDA reported notable improvements in math performance, along with enhanced confidence in math, increased self-assurance, more enjoyment of math, and reduced anxiety towards the subject.</p><p><strong>Empirical Evidence: High Usage Volume Correlates with Positive Outcomes</strong></p><p>The data, illustrated below, showed a clear distinction between high and low app usage. Those with higher usage rates were more likely to report an improvement in their academic and emotional relationship with math. Specifically, 72.8% of students with high app usage believed their math performance had improved, compared to 66.8% of those with fewer searches.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*O4XfamapelAsNK5vux6TQw.png" /></figure><p>*Note: Horizontal bars denote 95% confidence intervals.</p><p><strong>The Refined Study and Its Publication</strong></p><p>The refined version of this study has been published in the journal article: Ko, G. Y., Shin, D., Auh, S., Lee, Y., &amp; Han, S. P. (2023). Learning Outside the Classroom During a Pandemic: Evidence from an Artificial Intelligence-Based Education App. Management Science, 69(6), 3616–3649. <a href="https://pubsonline.informs.org/doi/abs/10.1287/mnsc.2022.4531">Access the study here</a>.</p><p><strong>Implications for Educational Stakeholders</strong></p><p>Our findings are critical for educators and policymakers, highlighting how AI-powered learning apps like QANDA can substantially benefit academic skill development and emotional well-being.</p><p><strong>Concluding Thoughts: The Transformative Power of Educational Technology</strong></p><p>This survey provided us with invaluable insights into the role of digital learning tools during a global crisis. The results, as detailed in both this blog post and our published journal article, underscore the significant positive impact that apps like QANDA can have on a student’s educational journey.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jSdvOvbSYLg9z0Ly4Qb1Lg.jpeg" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9911d1efaf73" width="1" height="1" alt=""><hr><p><a href="https://blog.mathpresso.com/the-connection-between-ai-powered-learning-app-usage-and-academic-confidence-a-national-survey-9911d1efaf73">AI Learning Apps and Academic Confidence: Insights from a National Survey</a> was originally published in <a href="https://blog.mathpresso.com">Team QANDA</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[미래 교실을 만드는 콴다의 AI 기술]]></title>
            <link>https://blog.mathpresso.com/%EB%AF%B8%EB%9E%98-%EA%B5%90%EC%8B%A4%EC%9D%84-%EB%A7%8C%EB%93%9C%EB%8A%94-%EC%BD%B4%EB%8B%A4%EC%9D%98-ai-%EA%B8%B0%EC%88%A0-ce351e77d112?source=rss----35e40785d286---4</link>
            <guid isPermaLink="false">https://medium.com/p/ce351e77d112</guid>
            <category><![CDATA[insights]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[digital-transform]]></category>
            <category><![CDATA[development]]></category>
            <dc:creator><![CDATA[Pyungwoo Yoo]]></dc:creator>
            <pubDate>Fri, 29 Mar 2024 09:36:12 GMT</pubDate>
            <atom:updated>2024-03-29T09:41:35.518Z</atom:updated>
            <content:encoded><![CDATA[<h4>AI 디지털 교과서를 통해 공교육 환경에서 교육 혁신을 만들어 갑니다.</h4><h3>콴다의 AI 기술, 교육의 미래를 열다</h3><p>전 세계 매달 천만 명의 학생들이 사용하는 콴다는 AI 기반 디지털 교과서를 통해 공교육 환경 내에서 교육 격차를 줄이고, 모든 학생이 자신의 잠재력을 최대한 발휘할 수 있는 맞춤형 교육을 제공하기 위해 노력하고 있습니다. 이를 위해 콴다는 국내 최고의 출판사와 협력하여 디지털 교과서 개발을 진행 중입니다. (<a href="https://edu.chosun.com/site/data/html_dir/2024/03/05/2024030580016.html"><em>콴다-비상교육, ‘AI 디지털교과서’ 개발 위한 MOU 체결</em></a><em>)</em></p><p>콴다는 맞춤형 교육을 통해 모든 학생이 자신의 가능성을 실현할 수 있도록 지원하며, AI 기술의 발전을 통해 교육의 미래를 새롭게 정의합니다.</p><h3>1. Document OCR: Digital Transformation</h3><p>콴다는 학생들이 제출한 문제 사진에서 정확한 정보를 추출할 수 있는 세계 최고 수준의 OCR(광학 문자 인식)기술을 보유하고 있습니다. 이 기술을 통해, 교과서와 같은 서적의 콘텐츠를 디지털 형태의 메타데이터로 변환할 수 있습니다.</p><p>콴다는 세 가지 주요 OCR 모델을 개발했습니다. 첫 번째 모델은 문제 영역에서 수식과 문자열 영역을 찾는 OCR (Text Detection OCR), 두 번째와 세 번째 모델은 식별된 수식과 문자열 영역에서 내용을 읽어내는 Expression OCR과 Text Recognition OCR입니다. 이 세 모델을 활용하여, 문서 내용을 웹에서 널리 사용되는 LaTeX 포맷을 포함한 형식으로 매우 정확하게 변환합니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*jT553Q9oj6wYPGuS" /></figure><h4>콘텐츠 분류의 첫 단계: Auto Boxing</h4><p>한 페이지 내에 다수의 문제가 포함된 경우, 콴다는 HWP, DOC, 또는 PDF 형식의 문서를 문항 단위로 나누는 데 사용되는 비전 기술을 활용합니다. 이를 통해 한 권의 책을 수 초 내에 문항별로 분리할 수 있습니다.</p><p><a href="https://youtube.com/shorts/k8qyjhsk9gA?feature=share">콴다 Auto Boxing</a></p><h4>정교한 Text Detection</h4><p>수학 문제의 내용을 정확하게 인식하는 콴다의 OCR 기술은 수식과 문자를 분리하여 각각의 영역을 정밀하게 인식합니다. 이 기술은 수학 문제에 특화되어 있으며, 문제 영역, 그림, 보기, 선택지까지 섬세하게 구분합니다. 저희는 이 기술을 Layout Analysis 라고 부릅니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*3-t54IpxyEklguHi" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*lHnZx5Wy6750UoJz" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*I80urAwQdMdJ9dpm" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*249tdmNqdHIopjz9" /></figure><h4>혁신적인 Expression OCR</h4><p>콴다는 복잡한 공식과 손글씨를 포함한 수학 문제를 LaTeX 포맷으로 변환하는 마법 같은 기술을 보유하고 있습니다. 이를 통해 사용자는 복잡한 수식도 쉽게 디지털화하여 처리할 수 있습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*lRETS6L7B0J-DcBb" /></figure><h4>다양한 언어의 Text Recognition</h4><p>콴다는 총 8개 언어(한국어, 베트남어, 태국어, 인도네시아어, 영어, 스페인어, 일본어, 포르투갈어)의 텍스트를 인식 할 수 있습니다. 이 다양한 언어 지원을 통해 글로벌 학습 환경을 제공합니다.</p><h3>2. 지능적인 분류를 통한 학습 효율 극대화: Auto Tagging:</h3><p>AI를 활용한 디지털 교과서는 학생의 성취도와 피드백에 반응형으로 전달할 때 가장 큰 가치를 줄 수 있습니다. 콴다의 VISION 기술로 변환된 콘텐츠는 교육용 AI 기술과 결합하여 맞춤형 학습의 가치를 극대화합니다.</p><h4>Transformer 기반 문제유형 분류기</h4><p>Transformer 기반 문제유형 분류 기술은 수학 문제의 유형과 난이도를 자동으로 분류합니다. 또한, 정답률과 풀이 시간 등 문항의 부가정보를 추론하여, 발행사가 학생에게 맞춤형 학습 경험을 제공할 수 있도록 지원합니다. 이 과정에서 교육부에서 발표한 표준 지식맵과 자체 구축한 지식맵을 연동했습니다. 이는 발행사의 지식맵이 표준 지식맵과 연동되어 있다면, 콴다가 개발한 AI 모델을 쉽게 활용할 수 있다는 의미입니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*bUe0il3cerqlIMDE" /></figure><h4>Human in the Loop</h4><p>AI 모델이 초기 학습 이후 새로운 데이터에 대해 학습하지 않아 성능이 저하되는 현상을 ‘모델의 성능 열화(Concept Drift)’ 또는 ‘지식 열화(Knowledge Degradation)’라고 부릅니다. 교육 분야에서도, 한번의 AI 모델 학습만으로는 수학능력시험이나 교과과정의 변화와 같은 최신 트렌드를 따라잡기 어렵습니다.</p><p>이 문제를 해결하기 위해, 한국의 도메인 전문가들이 모델의 추론 결과를 지속적으로 검증하고, 재학습을 위한 데이터를 제공하는 점진적인 Auto Tagging 모델 정확도 향상 파이프라인을 개발하여 적용하고 있습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*EQLlY6eNBUEEnvW2" /></figure><h4>Question Relation</h4><p>수학 과목에는 수학 개념을 어느 정도 이해할 수 있는 Transformer 기반의 LLM을 구축하여 실제 서비스에 활용 중입니다. 예를 들어, ‘a,b,c 는 양수’라는 표현은 수학적으로 0보다 큰 자연수를 의미합니다. 이처럼 수학적 도메인 지식을 바탕으로, 관련 개념을 동일하게 분류하고, 유사한 문제를 관리하는 데 이 모델을 사용할 수 있습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*wwZ863cta63oBuLa" /></figure><h3>3. 힌트 해설 자동 생성: 이해의 순간을 더 가까이에</h3><h4>세계 최대 규모의 문제 해설 데이터베이스</h4><p>콴다는 세상에서 가장 방대한 수학 문제 DB를 보유하고 있습니다. 수천만 개의 수학 문제 해설을 기반으로 새로운 문제의 힌트와 해설을 자동 생성합니다. 질의응답이 가능한 대화형 기술을 활용해 학습자의 이해를 돕는 맞춤 해설을 제공합니다.</p><h4>RAG 기반 LLM Chatbot</h4><p>자연스러운 대화가 가능한 챗봇은 존댓말, 반말 등 다양한 어투로 자연스러운 해설을 제공합니다. 마치 옆에서 선생님이 직접 설명해 주는 것 같은 친근하고 이해하기 쉬운 방식으로 학습자를 지원합니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*tYvfuZkLxsWh8DMe" /></figure><h3>4. 맞춤형 교육을 위한 기술</h3><p>이렇게 여러 기술을 결합함으로써, 각 학습자에게 맞춤화된 교육 경험을 제공할 수 있습니다. 이러한 접근 방식은 학습자의 개별적인 필요와 성취 수준에 맞춰 최적화된 학습 내용을 제공하는 것을 목표로 합니다.</p><h4>성취도 진단 및 분석</h4><p>특히, 학습자의 성취도를 평가하고 분석하는 진단 시스템은 콴다가 운영하는 학원에 적용되어 있습니다. 문제유형과 난이도 등의 메타데이터가 포함된 문제들을 활용하여, 다양한 개념과 난이도가 혼합된 맞춤형 문제 세트를 자동으로 생성할 수 있습니다. 학생이 시험을 완료하면, 그의 성취도와 상세한 분석 리포트를 몇 초 안에 제공할 수 있습니다. 이미 태깅된 문제들을 사용하기 때문에, 학생별로 어떤 문제 유형이나 개념에서 약점을 보이는지 바로 파악하고, 이에 대한 즉각적인 분석 리포트를 생성할 수 있습니다. 이를 통해 선생님들은 개별 학생 지도에 필요한 시간과 노력을 크게 절약할 수 있으며, 학생들은 언제든지 자신의 학습 리포트를 확인하여 성적 추이를 파악할 수 있습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*B6sLMLBoEHzuxc-T" /></figure><h4>개인화된 학습 콘텐츠 제공으로 교육 격차 해소</h4><p>학생 개인의 성취도 분석을 기반으로 맞춤형 콘텐츠를 제공함으로써, 교실 내 다양한 수준의 학생들에게 최적화된 학습 경험을 선사합니다. 천편일률적인 교육 방식은 상위권 학생에게는 지루함을, 학습에 어려움을 겪는 학생들에게는 부담을 줄 수 있습니다. 반면, AI 기반 코스웨어를 활용하여 단계별로 적합한 콘텐츠를 제공하면, 모든 학생이 자신의 수준에 맞는 학습을 통해 성장의 기회를 얻을 수 있습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*MKi_GaKRz_NC7QMk" /></figure><h3>AI와 함께하는 학습의 여정: 교육의 가능성 확장</h3><p>AI의 발전은 교육 패러다임의 변화를 이끌며, 콴다는 개인화된 학습 경험을 제공하여 학습자의 잠재력을 극대화할 수 있는 환경을 조성하고 있습니다. 이를 통해 전 세계 수백만 학생이 자신의 학습 속도에 맞춰 교육 격차를 극복하고, 개인의 가능성을 무한히 확장할 수 있게 되었습니다.</p><p>콴다의 AI 디지털 교과서는 단순히 지식을 전달하는 것을 넘어, 학습자가 독립적으로 문제를 해결하고 새로운 지식을 탐색할 수 있도록 돕는 도구를 제공합니다.</p><p>AI 기술을 통한 교육 혁신은 여기서 멈추지 않고, 콴다는 지속적인 연구와 개발을 통해 학습 효율성을 더욱 극대화하고 교육의 미래를 새롭게 열어갈 준비를 하고 있습니다.</p><p>교육의 미래로의 여정에 여러분도 함께해 주실 준비가 되셨나요? 콴다와 함께라면, 여러분의 교육적 여정이 더욱 풍부하고 의미 있는 경험이 될 것입니다. 함께 교육의 미래를 향한 여정에 동참해 주세요.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bRDvjLSRvBRZGWMoa7k6sw.jpeg" /></figure><p>🌏글로벌 Top 교육 앱 QANDA(콴다)를 함께 만들어 갈 분들을 기다리고 있습니다! ➡️ <a href="https://team.mathpresso.com/">공고 확인하기</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=ce351e77d112" width="1" height="1" alt=""><hr><p><a href="https://blog.mathpresso.com/%EB%AF%B8%EB%9E%98-%EA%B5%90%EC%8B%A4%EC%9D%84-%EB%A7%8C%EB%93%9C%EB%8A%94-%EC%BD%B4%EB%8B%A4%EC%9D%98-ai-%EA%B8%B0%EC%88%A0-ce351e77d112">미래 교실을 만드는 콴다의 AI 기술</a> was originally published in <a href="https://blog.mathpresso.com">Team QANDA</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[웹에서 프리 드로잉 밑바닥부터 구현하기]]></title>
            <link>https://blog.mathpresso.com/%EC%9B%B9%EC%97%90%EC%84%9C-%ED%94%84%EB%A6%AC-%EB%93%9C%EB%A1%9C%EC%9E%89-%EB%B0%91%EB%B0%94%EB%8B%A5%EB%B6%80%ED%84%B0-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-965a0d2253c1?source=rss----35e40785d286---4</link>
            <guid isPermaLink="false">https://medium.com/p/965a0d2253c1</guid>
            <category><![CDATA[drawing]]></category>
            <category><![CDATA[febe]]></category>
            <category><![CDATA[frontend]]></category>
            <category><![CDATA[canvas]]></category>
            <category><![CDATA[bezier]]></category>
            <dc:creator><![CDATA[Minseo Kang]]></dc:creator>
            <pubDate>Mon, 25 Mar 2024 07:49:51 GMT</pubDate>
            <atom:updated>2024-03-25T07:49:51.423Z</atom:updated>
            <content:encoded><![CDATA[<h4>자연스러운 프리 드로잉 알고리즘을 완성해 가는 과정</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Sm20qTgQv8Wj5h3CWcZXvA.png" /></figure><h3>들어가며</h3><p>콴다에서는 태블릿에서 문제를 풀고 피드백을 받는 솔루션을 제공하고 있습니다. 문제가 띄워진 대지 위에서 학생이 직접 풀이 과정을 적고 OMR에 답을 체크하며 태블릿에서도 실제로 문제를 푸는 듯한 경험을 제공합니다. 이때 iOS, Android, 웹을 포함한 다양한 환경에서 일관적이면서도 만족스러운 문제 풀이 경험을 제공하기 위해 웹의 Canvas API를 사용합니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rjiwN5-mNUW2SM-fNCJNWA.jpeg" /><figcaption>콴다과외 — 레벨 테스트</figcaption></figure><p>처음에는 외부 라이브러리를 이용해 구현하였지만, 라이브러리에서 커스텀이 가능한 범위의 한계, 데이터가 많아졌을 때 발생하는 성능적인 이슈 등 의존성으로부터 오는 문제점이 조금씩 발견되었습니다. 추가로 더 많은 프로젝트에서 사용할 수 있도록 모듈화의 필요성이 제기되면서 의존성 없이 밑바닥부터 구현하기로 하였습니다. 그 과정에서 자연스러운 프리 드로잉을 구현하기 위해 제가 실제로 겪었던 기술적인 Challenge들과 이를 해결해 가는 과정을 공유해보려고 합니다.</p><h3>차근차근 정복해보기</h3><h4>가장 기본적인 프리 드로잉</h4><p>웹에서 유저의 터치 이벤트가 발생했을 때 가장 기본적으로 참고할 수 있는 데이터는 포인트 좌표입니다. 그리고 유저가 펜을 잡고 자유롭게 그린다면 다수의 터치 이벤트가 발생하게 됩니다. 그렇기 때문에 저희가 유저로부터 받은 데이터를 이용해 제일 먼저 생각해 볼 수 있는 방법은 입력받은 점들을 선으로 이어주는 것입니다. <em>Pointer Move</em> 이벤트를 통해 입력받은 점이 하나씩 추가될 때마다 이전 점과 현재 점을 이어주기만 하면 됩니다. 아래의 과정을 거치며 가장 기초적인 드로잉 알고리즘을 완성할 수 있습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*eW61c_dBxpLlm7G7UPTaow.png" /><figcaption>점과 점을 직선으로 잇는다.</figcaption></figure><p>하지만 아무래도 점들이 직선으로 이어지다 보니 아래와 같이 부드럽지 않습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/526/1*zM0i2DYA_5KYDCIGVaW7Ng.png" /><figcaption>점과 점을 직선으로 이었을 때의 결과물</figcaption></figure><h4>1. 곡선화하기</h4><p>부드럽지 않다면 가장 첫 번째로 생각해 볼 수 있는 방법은 점들을 곡선으로 잇는 것일 겁니다. 그 중 프론트엔드 개발자가 CSS Animation을 다루며 한 번쯤 들어보았을 법한 <strong><em>베지어 곡선</em></strong>을 알아봅시다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/988/1*gYbS9UnEfjcgDmZmpZTj7A.png" /></figure><p>위 사진과 같이 두 점이 있다고 가정해 봅시다. 왼쪽 점과 오른쪽 점을 각각 0.7과 0.3만큼 혼합한다면 아래와 같은 위치에 새로운 점을 찍을 수 있습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/988/1*CGw5LASbliiV4rZCjRp_mg.png" /></figure><p>조금 더 보편적으로 두 점의 혼합 정도를 각각 1–t, t (0 ≤ t ≤ 1)라고 한다면 아래와 같이 표현할 수도 있겠네요!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/988/1*G9v8HK4Y21arOD8X5MCgew.png" /></figure><p>이처럼 t를 일정하게 조절하며 두 점 사이의 점을 추정하는 것을 <strong><em>선형 보간 (lerping)</em></strong>이라고 합니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/988/1*O_2TI3i4qtwEsCgWiY4-8Q.png" /><figcaption>선형 보간</figcaption></figure><p>그리고 t를 0부터 1까지 조절하며 선형 보간의 결과를 자취로 남긴다면 이를 <strong><em>1차 베지어 곡선</em></strong>이라고 할 수 있습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/988/1*5JwLvsgptAJHQQEpCmyQlg.png" /><figcaption>1차 베지어 곡선</figcaption></figure><p>하지만 이렇게 만들어진 1차 베지어 곡선은 흔히 우리가 곡선이라고 말하는 곡률이 전혀 없습니다. 그래서 1차 베지어 곡선 2개를 혼합하여 2차 베지어 곡선을 만들어줄 수 있습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/988/1*8cqtwrwtKCMGCK4StZrE_w.png" /><figcaption>2차 베지어 곡선</figcaption></figure><p>더 나아가 2차 베지어 곡선 2개를 혼합하여 3차 베지어 곡선을, 점 n(n ≥ 2)개를 이용해 n-1차 베지어 곡선을 만들 수 있습니다.</p><p>비로소 저희가 흔히 부드럽다고 얘기할 수 있는 곡선을 만들었습니다. 2차 베지어 곡선과 3차 베지어 곡선은 각각 Javascript의 Canvas API에서 제공하는 <em>quadraticCurveTo</em>, <em>bezierCurveTo</em> 함수를 이용해 만들 수 있으니 모든 준비는 끝났습니다. 이제 아래와 같이 드로잉 로직을 정리할 수 있습니다.</p><blockquote>1. 점이 3개 모일 때까지 기다린다.<br>2. 점 3개가 모이면 <em>quadraticCurveTo</em>를 이용해 2차 베지어 곡선을 그린다.<br>3. 1–2번을 반복한다.</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5PT9ChVuqtlzTK1ZRdKajg.png" /></figure><p>아래는 실제로 적용했을 때의 모습입니다. 생각보다 여전히 끊기는 느낌이 드는 것 같습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/386/1*DXgbaKlES04eE0p_haByFg.png" /><figcaption>베지어 곡선을 적용했을 때의 결과물</figcaption></figure><h4>2. 스플라인 부드럽게 하기</h4><p>아래의 예시를 확인해 봅시다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/988/1*k0wZ0MfcEU312wBP71KzkQ.png" /></figure><p>하나의 베지어 곡선으로 이어준 점 3개에 대해서는 부드러울지 몰라도 각 곡선끼리는 부드럽게 이어지지 않는 것을 확인할 수 있습니다. 즉, 곡선끼리 부드럽게 이어주는 작업이 필요합니다. 이에 대한 해결책으로 먼저 n차 베지어 곡선을 생각해 볼 수 있습니다. 하지만 유저가 펜을 그리며 1000개의 점이 Input으로 들어왔고 999차 베지어 곡선으로 이어본다고 생각해 봅시다. 매우 비싼 작업이면서도 펜을 놓지 않았을 때 현재 찍힌 점에 의해 이전에 그렸던 선 또한 영향을 받게 됩니다. 저희가 원하는 작업은 가장 아름다운 곡선을 만들기보단 비싸지 않으면서도 Greedy하게 곡선을 그려주는 것이기 때문에 새로운 방법이 필요해 보입니다.</p><p>다시 2차 베지어 곡선으로 이루어진 스플라인으로 넘어가 봅시다. 여기서 곡선과 곡선을 이어주는 접점에 초점을 맞추어보려고 합니다. 만약 저 접점이 그 이전 점과 다음 점의 중점에 위치한다면 어떻게 될까요?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/988/1*3IfV_GzbET7Q1ZhPqw9U9w.png" /></figure><p>아래의 증명 과정을 거쳐 이전 곡선의 끝점 순간 기울기와 다음 곡선의 시작점 순간 기울기가 똑같아지는 것을 확인할 수 있습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/988/1*ohg3XL0-vnJfJAnAftb8kw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*L0SfhZ5bYL2uNBCznNJ-Fw.png" /><figcaption>증명 과정</figcaption></figure><p>그렇습니다! 접점의 위치를 교정해주면서 스플라인을 부드럽게 만들어주었습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/344/1*2cieyt39r9j1-KJ4l_2Q7Q.png" /><figcaption>접점 교정 후의 결과물</figcaption></figure><h4>3. 즉각적으로 드로잉 보여주기</h4><p>이제 드로잉 결과물은 만족스러워졌습니다. 하지만 영상으로 본다면 아직 어색한 느낌이 듭니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/752/1*rPMnPZZCNmwm4dP3LzsQww.gif" /><figcaption>드로잉이 포인터를 따라간다는 느낌을 준다.</figcaption></figure><p>아무래도 점 3개가 그려질 때까지는 어떠한 선도 보이지 않기 때문에 유저의 드로잉 이벤트에 느리게 반응하는 것처럼 보입니다. 이는 사용자 경험상 버벅이는 듯한 느낌을 주기 때문에 2차 베지어 곡선이 그려지기 전에 캔버스는 여전히 유저의 Input을 받고 있다는 것을 보여줘야 합니다. 따라서 점이 1개일 때 원, 점이 2개일 때 직선, 점이 3개일 때 2차 베지어 곡선을 그려주며 드로잉 이벤트에 즉각적으로 반응하도록 작업해 봅시다. <strong><em>2번 방법(스플라인 부드럽게 하기)</em></strong>과 합쳐진다면 아래와 같은 Flow를 타겠네요!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*osSxBZr_LEhe9Ha55Ehh9w.png" /></figure><p>그 결과, 아래와 같이 즉각적으로 반응하면서도 부드러운 드로잉이 완성되었습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/752/1*nqt4tlQS-1RxocnUoTgXVw.gif" /><figcaption>포인터 위치에 따라 즉각적으로 드로잉이 그려진다.</figcaption></figure><h4>4. Pen Event 대응하기</h4><p>지금까지 테스트한 환경은 PC Chrome + Mouse이었습니다. 위와 같은 일련의 알고리즘을 통해 만족스러운 결과물을 얻어냈지만 해당 드로잉 모듈이 가장 많이 사용될 Mobile Safari + Pen 환경에서는 막상 아래와 같이 천천히 그렸을 때 자글자글해지는 걸 볼 수 있습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0UbW4v1FLGbbp4tZARdx-Q.jpeg" /><figcaption>Mobile Safari + Pen 환경에서의 결과물</figcaption></figure><p>일반적으로 터치 혹은 마우스 이벤트가 발생하면 포인트 좌표가 달라졌을 때 Pointer Move 이벤트가 발생합니다. 하지만 Pen의 경우 동일한 포인트라도 pressure, tilt 등 좌표 외적인 속성들이 달라질 수 있기 때문에 동일한 좌표에서 여러 개의 Pointer Move 이벤트가 발생할 수 있습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/410/0*FkhcwICSttJYn9p9" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/396/0*4jyFISfrHJ2smNnf" /><figcaption>동일한 좌표임에도 펜압이 달라지니 연속적으로 Pointer Move 이벤트가 발생한다.</figcaption></figure><p>결국 중복된 점에 곡선이 그려졌기 때문에 일련의 알고리즘이 속수무책이 되어버렸습니다. 하지만 저희는 펜의 감도를 포함한 전문적인 드로잉 기술을 필요로 하지 않기 때문에 동일한 좌표일 때는 이벤트를 재호출하지 않도록 처리했습니다.</p><pre>if (<br>  currentPoint.x === previousPoint.x &amp;&amp;<br>  currentPoint.y === previousPoint.y<br>) {<br>  return;<br>}</pre><h4>5. 인접한 점 간소화하기</h4><p>그런데도 천천히 그렸을 때 Mobile Safari에서 여전히 자글자글하게 보이는 현상이 발생했습니다. 심지어 PC Safari에서도 동일한 현상이 발생했습니다. 이는 Chrome과 Safari에서 Pointer Move 이벤트를 서로 다르게 내려주는 데에서 원인을 찾을 수 있는데 아래와 같이 Chrome에서는 꽤 정밀한 좌표 데이터를 내려주지만, Safari에서는 좌표를 정수화하여 내려주는 것을 확인할 수 있습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/348/0*HUQZamP3IVOa21B-" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/534/0*TNMc-v6i1AtO0zPs" /><figcaption>(좌: Safari, 우: Chrome)</figcaption></figure><p>그 결과, 아래와 같은 과정을 거치며 보정이 들어갔음에도 자글자글해지게 됩니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*sivSJ4rm_enYWEd2fSyZzQ.png" /><figcaption>대각선으로 그린 선이 자글자글해지는 과정 (🔵 : 실제 터치 위치)</figcaption></figure><p>그래서 <strong><em>인접</em></strong>이라는 개념을 도입하였습니다. 사실 좌표가 정수화되었을 때 제일 큰 문제는 미시적인 관점에서 해당 좌표 데이터를 신뢰할 수 없다는 데에 있습니다. 즉, 직선을 의도하고 그렸더라도 인접한 점들은 반올림되어 직선의 형태가 나오지 않습니다. 따라서 두 점이 인접하다면 오차가 발생할 수 있다고 가정하고 그 중간 점을 생략하기로 했습니다. 이때 두 점 사이의 거리가 <em>sqrt(10) (상하좌우로 1~3px)</em>이내면 인접하다고 정의하였고 그 안에서의 보정은 사람이 인지하기 어렵다고 판단했습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2L_JJeK4Vfyh5eG4B6wX-w.png" /><figcaption>인접한 점을 지운 이후 (🔵 : 실제 터치 위치)</figcaption></figure><p>위와 같은 과정을 거치며 선들은 인접하지 않은 점들을 연결하고, 아래와 같이 천천히 그리더라도 자글거리지 않는 드로잉 결과물을 만들 수 있었습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/769/1*OPS8-NKP89BG3ElkRgv5Cg.jpeg" /><figcaption>인접한 점을 지웠을 때의 결과물</figcaption></figure><h4>6. 인접하지 않은 점 쪼개기</h4><p>이제 어느 플랫폼에서 어떻게 그리든 꽤 부드럽고 자연스럽게 그려지는 것 같습니다. 하지만 아래 영상과 같이 빠른 속도로 곡률이 크게 드로잉을 하면 보정 효과가 과하게 들어가는 듯한 느낌이 보입니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/836/1*OqmOTZRPQdNYcYfqn7y7EQ.gif" /><figcaption>원을 빠르게 그릴 때 보정 효과가 눈에 띈다.</figcaption></figure><p>바로 <strong><em>2번 방법(스플라인 부드럽게 하기)</em></strong>과 <strong><em>3번 방법(즉각적으로 드로잉 보여주기)</em></strong>을 적용하며 생겨난 Side effect인데 곡률이 큰 경우 아래와 같은 과정을 거치며 사용자에게 잔상이 남아 보였습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Jfh8elu1KeLMQjcFvZmpWQ.png" /></figure><p>이를 해결하기 위해 <strong><em>5번(인접한 점 간소화하기)</em></strong>에서 도입한 <strong><em>인접</em></strong>을 기준으로 두 가지 관점을 설정해 보았습니다.</p><blockquote><strong>미시적</strong> : 자글거리지 않아야 하고 보정 효과가 들어가도 사람이 인지하기 어렵다.<br><strong>거시적</strong> : 실제 그린 것과 유사해야 하고 보정 효과가 들어가면 사람이 인지하기 쉽다.</blockquote><p><strong><em>5번 방법(인접한 점 간소화하기)</em></strong>을 적용했으니 이제 캔버스에 남는 점들은 적어도 인접하지 않습니다. 인접하지 않다는 것은 남아있는 점들을 선으로 이을 땐 거시적인 관점을 고려해야 한다는 것과 동일합니다. 이때 <strong><em>2번 방법(스플라인 부드럽게 하기)</em></strong>은 거시적인 관점에서 보정 효과가 크다고 생각했고 여기서는 실제 그린 것과 유사하게 보정이 들어가야 자연스럽다는 결론에 도달했습니다. 따라서 2번 방법에서 접점을 옮기는 대신 2번째 점과 3번째 점의 중점을 만들어 해당 중점을 접점으로 두고 2차 베지어 곡선을 그리는 방식으로 살짝 알고리즘을 수정했습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Eee4M_Radm_4mzQlpzIkWA.png" /></figure><p>그 결과 아래와 같이 빠르게 원을 그렸을 때도 어색하지 않아졌습니다.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/784/1*rDPKKQiRHVrdAhrfJIwEzQ.gif" /><figcaption>원을 빠르게 그릴 때 보정 효과가 비교적 덜해보인다.</figcaption></figure><h3>마치며</h3><p>지금까지 사용자의 드로잉 데이터가 위 6가지의 보정 과정을 거치면서 웹 캔버스에 자연스럽게 노출되는 과정을 소개했습니다. 개인별로 선호하는 필기감이 다르기에 가장 이상적인 드로잉 알고리즘을 적용하기란 꽤 어려운 일입니다. 그럼에도<strong><em>학생이 태블릿에서 문제를 푸는 과정이 자연스러워야 한다</em></strong>는 목적을 달성해 나가며 다양한 사람들의 의견을 듣고 수많은 테스트를 진행하는 과정은 흥미로웠습니다. 콴다 팀은 여전히 개선점을 찾고 있으며 더 자연스러운 드로잉을 제공하기 위해 지금도 다양한 방법을 시도해 보고 있습니다.</p><p>🌏글로벌 Top 교육 앱 QANDA(콴다)를 함께 만들어 갈 개발자들을 기다리고 있습니다! ➡️ <a href="https://team.mathpresso.com/">공고 확인하기</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=965a0d2253c1" width="1" height="1" alt=""><hr><p><a href="https://blog.mathpresso.com/%EC%9B%B9%EC%97%90%EC%84%9C-%ED%94%84%EB%A6%AC-%EB%93%9C%EB%A1%9C%EC%9E%89-%EB%B0%91%EB%B0%94%EB%8B%A5%EB%B6%80%ED%84%B0-%EA%B5%AC%ED%98%84%ED%95%98%EA%B8%B0-965a0d2253c1">웹에서 프리 드로잉 밑바닥부터 구현하기</a> was originally published in <a href="https://blog.mathpresso.com">Team QANDA</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What Did USC Students Think of Cramify?]]></title>
            <link>https://blog.mathpresso.com/usc-students-dish-on-prep-pie-find-out-how-prep-pie-changed-their-study-game-d8fd5c168266?source=rss----35e40785d286---4</link>
            <guid isPermaLink="false">https://medium.com/p/d8fd5c168266</guid>
            <category><![CDATA[exam-prep]]></category>
            <category><![CDATA[llm]]></category>
            <category><![CDATA[insights]]></category>
            <category><![CDATA[product]]></category>
            <category><![CDATA[preppie]]></category>
            <dc:creator><![CDATA[Gyu Lee]]></dc:creator>
            <pubDate>Fri, 23 Feb 2024 08:24:12 GMT</pubDate>
            <atom:updated>2024-05-29T01:01:42.998Z</atom:updated>
            <content:encoded><![CDATA[<h3>🏇What Did USC Students Think of Cramify?</h3><h4>We asked USC students what they thought of Cramify</h4><h4>😎Justin N (Sophomore / Accounting Major)</h4><p>Leading up to my final exams, I felt overwhelmed with the immense amount of content across all my courses. I had plenty of notes for some courses but didn’t know where to start, while for others, I hadn’t been as diligent and lacked material to review. Even with resources on Blackboard, going through lecture slides and handouts was time-consuming.</p><p>When I first started using Cramify, I found the platform very intuitive and the ability to import my own notes invaluable. It created a condensed study guide that saved me significant time, time that was better spent on actual practice and understanding.</p><p>My experience with Cramify was extremely positive. No other study product I’ve seen compiles existing notes and resources into such a nuanced and concise study guide. It made understanding the concepts behind problems much easier and I see great potential with its AI problem solver and Prep Sets, offering more reliable solutions than GPT.</p><p>I’m excited about Cramify’s potential to expand into different disciplines, especially those that are less quantitative. Seeing it support students in areas like Psychology, History, and Humanities before I graduate would be great, but I hope the team focuses on delivering a comprehensive and quality product.</p><blockquote>So Check it out! Cramify has been more helpful than ChatGPT in my accounting studies, contributing to my success in finals and securing an overall A. The potential for student aid is immense and promising.</blockquote><h4>🤩Cameron (Sophomore / Business Major)</h4><p>Filtering out irrelevant material and trying to figure out how practice test questions relate to my answer key was frustrating. Every class had its own structure, making organizing materials feel like a puzzle.</p><p>When I first used Cramify, I was amazed at how easily it condensed and organized my material by topic. It quickly helped me get into the studying flow, even with complicated class material.</p><p>I found Cramify’s applications are directly useful as a full-time student, focusing on study prep and homework help rather than trying to be a jack of all trades.</p><p>I see a bright future for Cramify, aiming to improve its versatility and speed. I believe it’s a product that can cater to the average student’s study style for many generations.</p><p>Cramify is poised to integrate deeply into students’ lives, offering study sets, homework solutions, and practice exams.</p><blockquote>It’s made my studying much easier, and I believe it can do the same for others.</blockquote><h4>🥳Tommy (Sophomore / Business Major)</h4><p>The most significant issue faced was the sheer task of managing time effectively. The curriculum, each demanded a distinct study approach, making it difficult to retain information and apply it across different contexts. As the semester progressed, maintaining both concentration and energy became strenuous, leading to superficial engagement with the material.</p><p>The future with a product like Cramify sounds interesting, especially if it could make studying less of a chore and more personalized. The idea of having a tool that’s mine, tailored to how I study, is exciting.</p><p>Cramify team is not just throwing out another study app; they’re listening to what we need and trying to make studying less boring and more about us. It’s more than just getting your homework done quicker; it’s about making the whole learning experience feel more tailored to you. The team is really putting their hearts into making this happen, and that’s pretty cool!</p><h4>🥰Lilian:</h4><p>I was frustrated when the professor’s homework and test materials were way harder than what they taught in lectures, leading to a lot of self-studying and time spent looking for resources. Studying for tests felt risky without knowing what to expect.</p><p>When I first encountered Cramify, I was wary of its AI’s accuracy. However, it proved to be reliable, directing me to the uploaded textbook material for better understanding. Its ability to generate similar questions to what I uploaded was particularly appealing, boosting my confidence in test-taking.</p><p>This semester, I have professors who don’t cover everything in class, and I definitely can see how Cramify will be useful to me. It’s innovative, leveraging AI in ways other study tools don’t, by analyzing inputted material to inform its answers and organizing work effectively. It supplements learning and helps save students’ time.</p><p>Cramify is a valuable tool for supplementing classroom learning and preparing for exams. Its feature of providing explanations and practice questions is incredibly useful for gaining confidence and understanding the material better.</p><h4>😃Pablo (Sophomore / Health Science Major)</h4><p>One of the biggest points of frustration for me during exam preparation was covering an overwhelming amount of material within a limited time frame. It is difficult to overcome these due to several factors, including a lack of personalized guidance/support and ineffective study habits or time management skills.</p><p>When I first used Cramify, a few elements stuck out to me as appealing, particularly its ability to process large amounts of content into condensed notes with relative relevancy. The simplicity of condensing vast amounts of complex content into much more manageable notes would make my life as a student much easier than it currently is.</p><blockquote>Cramify improved my studying routine. Compared to ChatGPT, or other AI services, Cramify was able to provide accurate results based on exactly what was being discussed in class.</blockquote><p>I can see myself using it for other classes to help study for complicated content. It is extremely useful for study preparation and should make the process of study easier!</p><h3>Be the first to use Cramify</h3><p>🙋Cramify is coming this March! Sign up to get an early invitation 🎊<a href="https://cramify.ai/"><strong>here</strong></a>🎊</p><h3>Learn More About Us</h3><p>We, Team QANDA, believe that quality education is a right for every student, not a privilege. We are dedicated to delivering personalized educational resources to students worldwide, making learning accessible, engaging, and impactful for all.</p><p>Discover more about our journey on</p><ul><li>Our <a href="https://mathpresso.com/en">website</a></li><li>Forbes interview <a href="https://www.forbes.com/sites/rubyleung/2021/07/30/this-softbank-backed-korean-edtech-startup-helps-solve-your-math-problems-with-ai/?sh=2c4c437a15e3">article</a></li><li><a href="https://drive.google.com/drive/folders/1QVwSmWHBv7iHgt2nsWC8A6nPs3xvBLKJ?usp=drive_link">Company brief</a></li><li><a href="http://blog.cramify.ai/">Cramify Official Blog</a></li></ul><p>📖Check out <a href="https://cramify.ai/">Cramify</a>, the ultimate study tool to ace your exams!</p><p>#Cramify #QANDA #GenAI</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/0*sraadgjIN7gqo3DC" /></figure><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d8fd5c168266" width="1" height="1" alt=""><hr><p><a href="https://blog.mathpresso.com/usc-students-dish-on-prep-pie-find-out-how-prep-pie-changed-their-study-game-d8fd5c168266">What Did USC Students Think of Cramify?</a> was originally published in <a href="https://blog.mathpresso.com">Team QANDA</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>