<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>JoyDev</title>
    <link>https://joyjaewon.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Sun, 10 May 2026 04:38:56 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>JoyJaewon</managingEditor>
    <item>
      <title>AngularJS: Implementing a Reusable Toggle Chevron with MVC Architecture</title>
      <link>https://joyjaewon.tistory.com/21</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;AngularJS.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;855&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dczqBP/btsHHg0QxxR/WwkKzDwMuPQ6BBpCdavWb0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dczqBP/btsHHg0QxxR/WwkKzDwMuPQ6BBpCdavWb0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dczqBP/btsHHg0QxxR/WwkKzDwMuPQ6BBpCdavWb0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdczqBP%2FbtsHHg0QxxR%2FWwkKzDwMuPQ6BBpCdavWb0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;855&quot; data-filename=&quot;AngularJS.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;855&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In this blog post, we'll delve into how to implement a reusable toggle chevron feature in AngularJS, which is particularly handy for collapsible panels or dropdowns. This example will follow the Model-View-Controller (MVC) pattern, ensuring our code is both manageable and scalable. We will create a controller that handles the logic, use a directive for our view, and maintain the state in our model.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AngularJS MVC Structure and Implementation&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The MVC architecture separates concerns, making our applications easier to manage and extend:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Model&lt;/b&gt;: Manages data and rules. Here, the model will keep track of whether sections are expanded or collapsed.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;View&lt;/b&gt;: Displays data (model) and sends user actions (events) to the controller.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Controller&lt;/b&gt;: Updates the model based on user actions captured in the view and updates the view in response to model changes.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Step-by-Step Implementation&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. HTML Structure&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;We start by defining our HTML structure that will utilize our AngularJS components. We'll pass chevronId through to our AngularJS code, ensuring we can uniquely identify and control our toggle chevron.&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre id=&quot;code_1716990112038&quot; class=&quot;html xml&quot; data-ke-language=&quot;html&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;div ng-app=&quot;toggleApp&quot; ng-controller=&quot;ToggleController&quot;&amp;gt;
    &amp;lt;div&amp;gt;
        &amp;lt;a ng-click=&quot;toggleChevron()&quot;
           class=&quot;btn btn-chevron_icon&quot;
           data-bs-toggle=&quot;collapse&quot;
           href=&quot;#{{chevronId}}&quot;
           role=&quot;button&quot;
           aria-expanded=&quot;{{isTableExpanded}}&quot;
           aria-controls=&quot;{{chevronId}}&quot;
           ng-class=&quot;{'disabled': itemCollection.length == 0, 'btn-chevron_icon--disabled': itemCollection.length == 0}&quot;&amp;gt;
            &amp;lt;i ng-class=&quot;{'fa fa-chevron-up': isTableExpanded, 'fa fa-chevron-down': !isTableExpanded}&quot;&amp;gt;&amp;lt;/i&amp;gt;
        &amp;lt;/a&amp;gt;
        Address
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. AngularJS Controller&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The controller will handle the logic for toggling the chevron and listening to changes in the collection that might affect the visibility and state of the chevron.&lt;/p&gt;
&lt;pre id=&quot;code_1716990165591&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;angular.module('toggleApp', [])
.controller('ToggleController', ['$scope', '$attrs', function ($scope, $attrs) {
    $scope.chevronId = $attrs.chevronId;
    $scope.isTableExpanded = true;

    $scope.toggleChevron = function() {
        $scope.isTableExpanded = !$scope.isTableExpanded;
    };

    $scope.$watch('itemCollection.length', function (newLength) {
        $scope.isTableExpanded = newLength &amp;gt; 0;
    });
}]);&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;div&gt;&amp;nbsp;&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. Explanation of the Code&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;The ToggleController initializes with a default expanded state (isTableExpanded = true).&lt;/li&gt;
&lt;li&gt;toggleChevron is a function bound to the click event of the chevron link, which toggles the state of isTableExpanded.&lt;/li&gt;
&lt;li&gt;A $watch is set up on itemCollection.length to automatically collapse the chevron if the collection is empty, demonstrating responsive UI updates based on data changes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;By encapsulating the toggle logic in a directive and handling the state through a controller, this AngularJS implementation follows the MVC pattern, ensuring that each aspect of the application is properly isolated but works cohesively. This approach enhances maintainability and scalability, allowing other developers to easily understand and reuse components across the application.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>angularjs</category>
      <author>JoyJaewon</author>
      <guid isPermaLink="true">https://joyjaewon.tistory.com/21</guid>
      <comments>https://joyjaewon.tistory.com/21#entry21comment</comments>
      <pubDate>Wed, 29 May 2024 22:28:16 +0900</pubDate>
    </item>
    <item>
      <title>Reflecting on 2023: A Year of Growth and Learning</title>
      <link>https://joyjaewon.tistory.com/16</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;christopher-gower-m_HRfLhgABo-unsplash.jpg&quot; data-origin-width=&quot;3882&quot; data-origin-height=&quot;2584&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cuTRF2/btsC4xr7J9k/CmR3HDK2F23DBfnkfu1fh0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cuTRF2/btsC4xr7J9k/CmR3HDK2F23DBfnkfu1fh0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cuTRF2/btsC4xr7J9k/CmR3HDK2F23DBfnkfu1fh0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcuTRF2%2FbtsC4xr7J9k%2FCmR3HDK2F23DBfnkfu1fh0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;3882&quot; height=&quot;2584&quot; data-filename=&quot;christopher-gower-m_HRfLhgABo-unsplash.jpg&quot; data-origin-width=&quot;3882&quot; data-origin-height=&quot;2584&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2023 was a remarkable year for me &amp;ndash; a period brimming with growth, both personally and professionally. As I sit down to pen this retrospective, it strikes me just how far I've come, particularly in my role at the company where I delved deep into the world of software development. &lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;Embracing Full-Stack Development &lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The year marked a significant shift in my career. I transitioned from handling small-scale projects to taking charge of entire pages, a move that saw me diving into C#. This was more than just learning a new language; it was about understanding the intricate web of our company's codebase. I began navigating files with ease, uncovering the connections between JSON and C#, unraveling the mysteries of APIs, and applying concepts like `GET`, `PUT`, and `POST` that I'd only encountered in academic settings. &lt;br /&gt;&lt;br /&gt;My work on the Directory and Mortgage Call Report Pages as the sole developer was particularly enlightening. These projects honed my problem-solving skills and taught me the value of thorough research and seeking guidance from senior developers. They were exercises in critical thinking, pushing me to consider not just how to make code work, but how to make it efficient and clean. &lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;A Broader Perspective &lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2023 expanded my horizon beyond UI and UX design and simple AngularJS scripts. I delved into the full-stack cycle, gaining a broader perspective on project development. This shift in viewpoint wasn't just about seeing the bigger picture; it also led me to think more structurally. Where my earlier approach was just about getting code to work, I now focus on writing cleaner, more efficient code. &lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Continuous Learning and Side Projects &lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Outside of work, I kept the momentum going. I completed a 10-week study in React, bolstering my frontend skills, and embarked on various side projects, including developing my wedding website, cloning YouTube, and crafting a portfolio site. These endeavors not only supplemented my professional growth but also allowed me to explore and express my creativity in new ways. &lt;br /&gt;&lt;br /&gt;On the education front, I graduated with a master's in Computer Science, specializing in Data Science, in May. The journey through academia reaffirmed my belief in the endless depth and breadth of this field. The more I learn and engage in diverse projects, the more expansive and profound my understanding becomes. &lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;DSA and Mentoring &lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In my pursuit of deepening my knowledge in Data Structures and Algorithms (DSA), I joined the Formation platform. Here, I learned from top-tier industry mentors, dedicating time after work to study and grow. This experience was not just about acquiring new knowledge but also about understanding its practical application in real-world scenarios. &lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Looking Forward&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;As&amp;nbsp;I&amp;nbsp;look&amp;nbsp;back&amp;nbsp;on&amp;nbsp;2023,&amp;nbsp;I'm&amp;nbsp;filled&amp;nbsp;with&amp;nbsp;gratitude&amp;nbsp;for&amp;nbsp;the&amp;nbsp;experiences&amp;nbsp;and&amp;nbsp;lessons.&amp;nbsp;It's&amp;nbsp;been&amp;nbsp;a&amp;nbsp;year&amp;nbsp;of&amp;nbsp;substantial&amp;nbsp;growth,&amp;nbsp;challenges,&amp;nbsp;and&amp;nbsp;achievements.&amp;nbsp;And&amp;nbsp;yet,&amp;nbsp;I&amp;nbsp;know&amp;nbsp;there's&amp;nbsp;so&amp;nbsp;much&amp;nbsp;more&amp;nbsp;to&amp;nbsp;learn&amp;nbsp;and&amp;nbsp;many&amp;nbsp;more&amp;nbsp;heights&amp;nbsp;to&amp;nbsp;reach. &lt;br /&gt;I step into 2024 with excitement, ready to embrace new challenges, deepen my knowledge, and continue contributing to the ever-evolving world of technology. &lt;br /&gt;&lt;br /&gt;Here's&amp;nbsp;to&amp;nbsp;another&amp;nbsp;year&amp;nbsp;of&amp;nbsp;growth,&amp;nbsp;learning,&amp;nbsp;and&amp;nbsp;coding!&amp;nbsp; &lt;/p&gt;</description>
      <author>JoyJaewon</author>
      <guid isPermaLink="true">https://joyjaewon.tistory.com/16</guid>
      <comments>https://joyjaewon.tistory.com/16#entry16comment</comments>
      <pubDate>Sun, 31 Dec 2023 15:11:25 +0900</pubDate>
    </item>
    <item>
      <title>AngularJS: Validating Data Before Updating the API</title>
      <link>https://joyjaewon.tistory.com/15</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;AngularJS logo.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;855&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvBo8m/btsv8SDP3XA/uicDvRHOLgEQkfxIHnRLXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvBo8m/btsv8SDP3XA/uicDvRHOLgEQkfxIHnRLXk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvBo8m/btsv8SDP3XA/uicDvRHOLgEQkfxIHnRLXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvBo8m%2Fbtsv8SDP3XA%2FuicDvRHOLgEQkfxIHnRLXk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;855&quot; data-filename=&quot;AngularJS logo.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;855&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Last time, we went on a deep dive into importing CSV files, mapping the fields, and storing that data using AngularJS's `$http.post`. If you missed that part, make sure to&lt;a href=&quot;https://joyjaewon.dev/14&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt; catch up here&lt;/a&gt; . Today, as promised, we'll focus on a critical aspect of any data manipulation process: validation.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;Why Data Validation is Important&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;First things first: Why do we even need to validate data? Simply put, garbage in equals garbage out. If you start with inaccurate or incomplete data, no amount of fancy algorithms or complex systems can make your application function correctly. Validation ensures that the data being sent to your API meets certain criteria, reducing the likelihood of errors and ensuring integrity.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;Implementing Basic Validation Logic&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Before you hit the &quot;Save&quot; button and make the POST request to the API, you should make sure that the data is valid. Let's start by adding some basic checks in our controller.&lt;br /&gt;&lt;br /&gt;Here's&amp;nbsp;an&amp;nbsp;updated&amp;nbsp;`$scope.validateData`&amp;nbsp;function:&lt;/p&gt;
&lt;pre id=&quot;code_1696028270261&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$scope.validateData = function(allItems) {
    let isValid = true;
    let errorMessages = [];

    allItems.forEach(function(item, index) {
        if (!item.FirstName || !item.LastName || !item.Email) {
            isValid = false;
            errorMessages.push(`Missing required fields in item at index ${index}`);
        }

        if (item.Email &amp;amp;&amp;amp; !emailValidator(item.Email)) {  // Assume emailValidator is a function to validate emails
            isValid = false;
            errorMessages.push(`Invalid email format in item at index ${index}`);
        }

        // Add more validation logic here
    });

    return { isValid, errorMessages };
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;In&amp;nbsp;the&amp;nbsp;example&amp;nbsp;above,&amp;nbsp;`emailValidator`&amp;nbsp;could&amp;nbsp;be&amp;nbsp;a&amp;nbsp;simple&amp;nbsp;function&amp;nbsp;that&amp;nbsp;uses&amp;nbsp;a&amp;nbsp;regular&amp;nbsp;expression&amp;nbsp;to&amp;nbsp;check&amp;nbsp;if&amp;nbsp;the&amp;nbsp;email&amp;nbsp;address&amp;nbsp;is&amp;nbsp;in&amp;nbsp;a&amp;nbsp;valid&amp;nbsp;format.&amp;nbsp;We're&amp;nbsp;checking&amp;nbsp;for&amp;nbsp;missing&amp;nbsp;required&amp;nbsp;fields&amp;nbsp;(`FirstName`,&amp;nbsp;`LastName`,&amp;nbsp;and&amp;nbsp;`Email`)&amp;nbsp;and&amp;nbsp;invalid&amp;nbsp;email&amp;nbsp;formats.&amp;nbsp;If&amp;nbsp;any&amp;nbsp;item&amp;nbsp;fails&amp;nbsp;these&amp;nbsp;checks,&amp;nbsp;we&amp;nbsp;store&amp;nbsp;an&amp;nbsp;error&amp;nbsp;message&amp;nbsp;and&amp;nbsp;set&amp;nbsp;`isValid`&amp;nbsp;to&amp;nbsp;`false`.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;Using the Validation Function&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Before we proceed to save the data, let's make sure to call this validation function. Here's how to integrate it with the `$scope.saveCSV` function:&lt;/p&gt;
&lt;pre id=&quot;code_1696028307809&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$scope.saveCSV = function(allItems) {
    let validation = $scope.validateData(allItems);

    if (validation.isValid) {
        return $http.post('/api/YourEndpoint/', {
            Data: JSON.stringify(allItems)
        });
    } else {
        console.log('Validation failed:', validation.errorMessages);
        // Handle validation errors here
    }
};&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;That&amp;rsquo;s it! You now have a way to validate your data before making that crucial POST request. It's a simple but powerful addition to your data manipulation arsenal. Good validation is like a solid foundation; it may not be the most exciting part of the construction process, but without it, you're always at risk of everything tumbling down.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In the next post, I will talk about regex that can be used for the validation.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Until then, happy coding!&lt;/p&gt;</description>
      <category>AngularJS</category>
      <category>angularjs</category>
      <category>CSV Validation</category>
      <author>JoyJaewon</author>
      <guid isPermaLink="true">https://joyjaewon.tistory.com/15</guid>
      <comments>https://joyjaewon.tistory.com/15#entry15comment</comments>
      <pubDate>Sat, 30 Sep 2023 08:16:58 +0900</pubDate>
    </item>
    <item>
      <title>AngularJS: Importing CSV Files and Save Each Row</title>
      <link>https://joyjaewon.tistory.com/14</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;AngularJS.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;855&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dRhTVR/btssOtl9v8C/y6YiTh2f1fqvLEtAvuaiAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dRhTVR/btssOtl9v8C/y6YiTh2f1fqvLEtAvuaiAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dRhTVR/btssOtl9v8C/y6YiTh2f1fqvLEtAvuaiAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdRhTVR%2FbtssOtl9v8C%2Fy6YiTh2f1fqvLEtAvuaiAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;855&quot; data-filename=&quot;AngularJS.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;855&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Recently, I wrapped up the development of a directory page at work, and I learned so much from this project. The project entailed a multitude of tasks&amp;mdash;fetching data from an API, implementing sorting and searching functionalities, importing and validating CSV files, and updating the API through POST requests for CRUD operations. While these features might appear straightforward on the surface, diving deeper reveals a complex web of interconnected components.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;Since this project was such a rich learning experience, I've decided to document it for both reflection and future reference. In my previous posts, we explored how to convert JSON data to a CSV file and enable its download.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Today, we're going to delve into the &lt;u&gt;&lt;b&gt;process of importing CSV files, mapping their data fields&lt;/b&gt;&lt;/u&gt;&amp;mdash;such as Email, Title, and Phone&amp;mdash;and then saving them using AngularJS's `$http.post`. And in the next post, we will talk about validating the data before making the POST request to update the API.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #374151; text-align: left;&quot;&gt;Creating a Custom Directive for File Input in AngularJS.&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Before we get into the code, it's worth noting that AngularJS doesn't offer a native directive for file input handling. We'll need to create our own custom directive to bridge the gap between raw file input and AngularJS.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;Here's&amp;nbsp;a&amp;nbsp;directive&amp;nbsp;called&amp;nbsp;`fileModel`,&amp;nbsp;which&amp;nbsp;leverages&amp;nbsp;`$parse`&amp;nbsp;to&amp;nbsp;bind&amp;nbsp;the&amp;nbsp;file&amp;nbsp;input&amp;nbsp;to&amp;nbsp;a&amp;nbsp;scope&amp;nbsp;variable: &lt;/p&gt;
&lt;pre id=&quot;code_1693500250090&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;.directive('fileModel', ['$parse', function ($parse) {
    return {
        restrict: 'A',
        link: function (scope, element, attrs) {
            var model = $parse(attrs.fileModel);
            var modelSetter = model.assign;

            element.bind('change', function () {
                scope.$apply(function () {
                    modelSetter(scope, element[0].files[0]);
                });
            });
        }
    };
}])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This directive ensures that any file input element in our HTML can bind to a scope variable, making it available for our AngularJS code to process.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Importing and Mapping CSV Data &lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In your controller, you can define a function named `$scope.importCSV`. This function will do the heavy lifting of reading the file, breaking it into lines and headers, and mapping these to JSON objects.&lt;/p&gt;
&lt;pre id=&quot;code_1693500963606&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$scope.importCSV = function () {
    // Get the file from scope, populated by our 'fileModel' directive
    let file = $scope.myFile;
    if (file) {
        // Create a FileReader object
        let reader = new FileReader();
        
        // Define what happens when the file has been read
        reader.onload = function (event) {
            let contents = event.target.result;
            let lines = contents.split('\n');
            let headers = lines[0].split(',');
            let csvItems = [];

            // Loop through each line in the file
            let i = 1;
            while (i &amp;lt; lines.length) {
                let result = getNextRow(lines, i);  // Assume getNextRow is a function that gets the next row
                let row = result.row;
                i = result.nextIndex;

                // Skip empty lines
                if (row.join('').trim() === &quot;&quot;) continue;

                let obj = {};
                // Loop through each header and assign the corresponding value to our object
                for (let j = 0; j &amp;lt; headers.length; j++) {
                    obj[headers[j].trim()] = row[j] ? row[j].trim() : &quot;&quot;;
                }
                csvItems.push(obj);
            }

            // Map these objects to your desired format
            let allItems = [];
            for (let csvItem of csvItems) {
                let item = {
                    Id: generateId(),  // Assume generateId is a function that generates an ID
                    FirstName: csvItem[&quot;First Name&quot;],
                    LastName: csvItem[&quot;Last Name&quot;],
                    Email: csvItem[&quot;Email&quot;]
                    // Add more fields as necessary
                };

                allItems.push(item);
            }

            // Save these objects by calling a function to do an HTTP POST operation
            $scope.saveCSV(allItems);
        };
        
        // Initiate reading the file
        reader.readAsText(file);
    }
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This function is triggered when you want to import a CSV. It reads the file, iterates through the lines, and maps them to a list of JavaScript objects (`allItems`). Finally, it calls `$scope.saveCSV(allItems)` to save these objects.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Saving the Data&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Finally, the `$scope.saveCSV` function is called to save this mapped data:&lt;/p&gt;
&lt;pre id=&quot;code_1693500358130&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$scope.saveCSV = function (allItems) {
    return $http.post('/api/YourEndpoint/', {
        Data: JSON.stringify(allItems)
    });
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This function posts the mapped JSON data to the API, where it's stored for future CRUD operations. &lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;And that's a wrap for this installment! We've gone through the entire process of importing a CSV file, parsing its contents, and saving it to our API using AngularJS's &lt;/span&gt;$http.post&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;. In the next post, we'll discuss how to validate this data before we hit that &quot;Save&quot; button and send it to our API. Until then, happy coding!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>AngularJS</category>
      <category>angularjs</category>
      <category>ImportCSV</category>
      <author>JoyJaewon</author>
      <guid isPermaLink="true">https://joyjaewon.tistory.com/14</guid>
      <comments>https://joyjaewon.tistory.com/14#entry14comment</comments>
      <pubDate>Fri, 1 Sep 2023 02:01:54 +0900</pubDate>
    </item>
    <item>
      <title>AngularJS: Converting and Downloading JSON as CSV</title>
      <link>https://joyjaewon.tistory.com/13</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;AngularJS.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;855&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qZz5f/btsrhGAJkOz/kE0xEav7MfTKD65k6KVzjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qZz5f/btsrhGAJkOz/kE0xEav7MfTKD65k6KVzjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qZz5f/btsrhGAJkOz/kE0xEav7MfTKD65k6KVzjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqZz5f%2FbtsrhGAJkOz%2FkE0xEav7MfTKD65k6KVzjk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;855&quot; data-filename=&quot;AngularJS.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;855&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Today, I want to dive into an intriguing method that I've employed while working with the transition between CSV and JSON formats. As developers, we often encounter situations where data needs to be stored, processed, and then retrieved in a convenient format. In this blog post, I'm going to outline a technique that makes storing and retrieving data in a &lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;i&gt;CSV format from a JSON object&lt;/i&gt; &lt;/span&gt;&lt;/b&gt;a seamless process.&amp;nbsp; &lt;br /&gt;&lt;br /&gt;When I was assigned this task, I was wondering why not just upload and store the CSV file in the system? So I did some research, and I learned that storing data in the JSON format in our database rather than as raw CSV files provides a multitude of advantages:&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;1. &lt;b&gt;Readability and Structure&lt;/b&gt;: JSON format is both human-readable and easy to parse, making the stored data easily understandable and workable.&amp;nbsp; &lt;br /&gt;2. &lt;b&gt;Flexibility&lt;/b&gt;: JSON's structure allows for nested objects and arrays, making it a more flexible data representation. &lt;br /&gt;3. &lt;b&gt;Standardized Data Interchange&lt;/b&gt;: Many modern APIs use JSON for data interchange, making it the lingua franca of web-based data. &lt;br /&gt;4. &lt;b&gt;Storage Efficiency&lt;/b&gt;: When storing data in a structured database, JSON can often be more space-efficient than CSV, especially when there are many optional columns in your data. &lt;br /&gt;5. &lt;b&gt;Security&lt;/b&gt;: Parsing CSV is prone to injection attacks if not done carefully, while JSON parsing libraries are typically more robust and safe. &lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1. Converting JSON to CSV&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Before diving into the downloading part, let's first convert our JSON data to CSV. This function aptly named `jsonToCSV` takes care of that:&lt;/p&gt;
&lt;pre id=&quot;code_1692109212817&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function jsonToCSV(objArray) {
    const array = typeof objArray !== 'object' ? JSON.parse(objArray) : objArray;
    let csvString = '';
    
    if (array.length &amp;gt; 0) {
        const headers = Object.keys(array[0]).join(',');
        csvString += headers + '\r\n';
    }
    
    for (let item of array) {
        let line = '';
        for (let key in item) {
            if (line) line += ',';
            line += String(item[key]).includes(',') ? `&quot;${item[key]}&quot;` : item[key];
        }
        csvString += line + '\r\n';
    }
    return csvString;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The logic is quite simple:&amp;nbsp;&lt;br /&gt;-&amp;nbsp;We&amp;nbsp;first&amp;nbsp;ensure&amp;nbsp;that&amp;nbsp;the&amp;nbsp;data&amp;nbsp;we&amp;nbsp;have&amp;nbsp;is&amp;nbsp;an&amp;nbsp;array&amp;nbsp;of&amp;nbsp;objects. &lt;br /&gt;-&amp;nbsp;We&amp;nbsp;extract&amp;nbsp;the&amp;nbsp;headers&amp;nbsp;(i.e.,&amp;nbsp;object&amp;nbsp;keys)&amp;nbsp;and&amp;nbsp;append&amp;nbsp;them&amp;nbsp;as&amp;nbsp;the&amp;nbsp;first&amp;nbsp;row&amp;nbsp;of&amp;nbsp;our&amp;nbsp;CSV&amp;nbsp;string. &lt;br /&gt;-&amp;nbsp;We&amp;nbsp;then&amp;nbsp;iterate&amp;nbsp;over&amp;nbsp;each&amp;nbsp;object&amp;nbsp;(row&amp;nbsp;of&amp;nbsp;data)&amp;nbsp;and&amp;nbsp;append&amp;nbsp;its&amp;nbsp;values&amp;nbsp;to&amp;nbsp;our&amp;nbsp;CSV&amp;nbsp;string,&amp;nbsp;ensuring&amp;nbsp;that&amp;nbsp;data&amp;nbsp;containing&amp;nbsp;commas&amp;nbsp;is&amp;nbsp;wrapped&amp;nbsp;in&amp;nbsp;double&amp;nbsp;quotes. &lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2. Fetching Data and Prompting a Download&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Now, with our CSV string in hand, it's time to download the data. Here's an example function, integrated with AngularJS, that fetches JSON data from an API, converts it to CSV, and initiates a download:&lt;/p&gt;
&lt;pre id=&quot;code_1692109227599&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$scope.downloadSampleCSV = function () {
    $http.get('/api/Settings/User/SystemDefaults/Your CSV')
        .then(function(response) {
            const jsonData = response.data;
            const csvContent = jsonToCSV(jsonData);
            $scope.downloadCSV(csvContent, 'Sample.csv');
        });
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The function:&lt;br /&gt;-&amp;nbsp;Sends&amp;nbsp;a&amp;nbsp;GET&amp;nbsp;request&amp;nbsp;to&amp;nbsp;fetch&amp;nbsp;JSON&amp;nbsp;data. &lt;br /&gt;-&amp;nbsp;Uses&amp;nbsp;the&amp;nbsp;`jsonToCSV`&amp;nbsp;function&amp;nbsp;to&amp;nbsp;convert&amp;nbsp;this&amp;nbsp;data&amp;nbsp;to&amp;nbsp;CSV. &lt;br /&gt;- Calls another function (in this example, `$scope.downloadCSV`) to handle the CSV download.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;br /&gt;Happy coding!&lt;/p&gt;</description>
      <category>AngularJS</category>
      <category>angularjs</category>
      <category>JSON-CSV-Conversion</category>
      <author>JoyJaewon</author>
      <guid isPermaLink="true">https://joyjaewon.tistory.com/13</guid>
      <comments>https://joyjaewon.tistory.com/13#entry13comment</comments>
      <pubDate>Tue, 15 Aug 2023 23:26:58 +0900</pubDate>
    </item>
    <item>
      <title>AngularJS: Dealing with Async Updates using Promise.all() and $scope.$applyAsync()</title>
      <link>https://joyjaewon.tistory.com/12</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;AngularJS.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;855&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uCZFZ/btsravHcInK/C1e6YhnoiDqPh8PmaJERGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uCZFZ/btsravHcInK/C1e6YhnoiDqPh8PmaJERGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uCZFZ/btsravHcInK/C1e6YhnoiDqPh8PmaJERGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuCZFZ%2FbtsravHcInK%2FC1e6YhnoiDqPh8PmaJERGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;855&quot; data-filename=&quot;AngularJS.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;855&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;During my recent work on an AngularJS project, I faced a peculiar issue. I needed to fetch data from multiple URLs, process the results once they were all fetched, and then update my controller's scope with the processed data. However, due to JavaScript's asynchronous nature, I found my view not getting updated promptly, leading to inconsistencies in the user experience. Here's how I resolved this issue using `Promise.all()` and `$scope.$applyAsync()`.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Issue&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In my AngularJS application, I had to fetch data from a series of URLs. Each of these requests returned an object that I needed to process and then push into an array in my controller's scope. The array was then displayed in the application's view. The code for fetching and processing the data was something like this:&lt;/p&gt;
&lt;pre id=&quot;code_1690817996609&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$scope.getData = function () {
    $http.get('url')
        .then(function (response) {
            const responseData = response.data;
            const dataPromises = responseData.map(function (item) {
                return $http.get('item-url')
                    .then(function (response) {
                        let itemData = response.data;
                        $scope.myArray.push(itemData);
                    });
            });

            //...
        });
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The problem was that the view wasn't getting updated immediately when `$scope.myArray` was modified. This was because the `$http.get()` calls were asynchronous, and the view was getting rendered before the data was fully fetched and processed.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;br /&gt;Solution 1. Promise.all()&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Promise.all() is a built-in JavaScript function that takes an array of promises and returns a new promise that only resolves when all the promises in the array have resolved. This function was exactly what I needed to ensure that my view only got updated after all the data had been fetched and processed. I modified my code to use `Promise.all()` like this:&lt;/p&gt;
&lt;pre id=&quot;code_1690818016672&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$scope.getData = function () {
    $http.get('url')
        .then(function (response) {
            const responseData = response.data;
            const dataPromises = responseData.map(function (item) {
                return $http.get('item-url')
                    .then(function (response) {
                        let itemData = response.data;
                        return itemData;
                    });
            });

            Promise.all(dataPromises).then(function (allData) {
                $scope.myArray = allData;
                //...
            });
        });
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This solved part of the problem, but the view was still not getting updated immediately. This was because AngularJS wasn't aware that `$scope.myArray` was being modified inside the `Promise.all()` callback.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;br /&gt;Solution 2. $scope.$applyAsync()&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In AngularJS, `$scope.$apply()` is used to manually trigger a digest cycle, which checks for changes in the model and updates the view. But calling `$scope.$apply()` multiple times during the same digest cycle can lead to an error. `$scope.$applyAsync()`, on the other hand, schedules an apply operation that will occur later, which won't cause errors if it's called multiple times in the same digest cycle. &lt;br /&gt;&lt;br /&gt;I modified my code to use `$scope.$applyAsync()` inside the `Promise.all()` callback, ensuring that AngularJS would be aware of the changes to `$scope.myArray` and update the view accordingly:&lt;/p&gt;
&lt;pre id=&quot;code_1690818038797&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$scope.getData = function () {
    $http.get('url')
        .then(function (response) {
            const responseData = response.data;
            const dataPromises = responseData.map(function (item) {
                return $http.get('item-url')
                    .then(function (response) {
                        let itemData = response.data;
                        return itemData;
                    });
            });

            Promise.all(dataPromises).then(function (allData) {
                $scope.myArray = allData;
                $scope.$applyAsync();
            });
        });
};&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;And there you have it! By combining `Promise.all()` and `$scope.$applyAsync()`, I was able to fetch and process multiple sets of data asynchronously, update my controller's scope when all the data was ready, and ensure that the view was updated promptly and correctly. &lt;br /&gt;&lt;br /&gt;This approach demonstrates the power of JavaScript's Promise API and AngularJS's built-in services and tools. It allows us to manage complex asynchronous operations more easily and efficiently.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Happy Coding!&lt;/p&gt;</description>
      <category>AngularJS</category>
      <category>$scope.$applyAsync()</category>
      <category>angularjs</category>
      <category>promise.all()</category>
      <author>JoyJaewon</author>
      <guid isPermaLink="true">https://joyjaewon.tistory.com/12</guid>
      <comments>https://joyjaewon.tistory.com/12#entry12comment</comments>
      <pubDate>Tue, 1 Aug 2023 00:46:46 +0900</pubDate>
    </item>
    <item>
      <title>AngularJS: Handling Null and Empty Values with orderBy Filter</title>
      <link>https://joyjaewon.tistory.com/11</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;AngularJS.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;855&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/r2134/btsrdjNeZXo/ZH9X3kFoKV08QmKBbN1ulK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/r2134/btsrdjNeZXo/ZH9X3kFoKV08QmKBbN1ulK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/r2134/btsrdjNeZXo/ZH9X3kFoKV08QmKBbN1ulK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fr2134%2FbtsrdjNeZXo%2FZH9X3kFoKV08QmKBbN1ulK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;855&quot; data-filename=&quot;AngularJS.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;855&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Today, I want to share a powerful solution that I discovered while working with AngularJS's `orderBy` filter. Sorting lists is a common task in web development, and AngularJS offers a convenient `orderBy` filter to handle it. However, I stumbled upon a challenge when dealing with null or empty values during sorting. In AngularJS, orderBy filter doesn't provide a straightforward way to handle null or empty values in a special way, such as placing them at the end of the list. However, we can workaround this by creating a custom filter function. This custom function will separate the list into two arrays, sort both arrays, and then concatenate them.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;Here's a simplified version of the custom sorting function:&lt;/p&gt;
&lt;pre id=&quot;code_1690338716662&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;function customSort(itemList) {
    var withTheData = [];
    var nullData = [];

    for (var i = 0; i &amp;lt; itemList.length; i++) {
        if (itemList[i].orderKey !== null &amp;amp;&amp;amp; itemList[i].orderKey !== undefined) {
            withTheData.push(itemList[i]);
        } else {
            nullData.push(itemList[i]);
        }
    }

    withTheData.sort(sortFunction);
    nullData.sort(sortFunction);

    return withTheData.concat(nullData);
}
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In&amp;nbsp;the&amp;nbsp;code&amp;nbsp;above,&amp;nbsp;we&amp;nbsp;define&amp;nbsp;the&amp;nbsp;`customSort`&amp;nbsp;function,&amp;nbsp;which&amp;nbsp;takes&amp;nbsp;an&amp;nbsp;`itemList`&amp;nbsp;as&amp;nbsp;its&amp;nbsp;argument.&amp;nbsp;Inside&amp;nbsp;the&amp;nbsp;function,&amp;nbsp;we&amp;nbsp;declare&amp;nbsp;two&amp;nbsp;arrays:&amp;nbsp;`withTheData`&amp;nbsp;to&amp;nbsp;store&amp;nbsp;items&amp;nbsp;with&amp;nbsp;valid&amp;nbsp;`orderKey`,&amp;nbsp;and&amp;nbsp;`nullData`&amp;nbsp;to&amp;nbsp;store&amp;nbsp;items&amp;nbsp;with&amp;nbsp;null&amp;nbsp;or&amp;nbsp;undefined&amp;nbsp;`orderKey`.&lt;br /&gt;&lt;br /&gt;Next,&amp;nbsp;we&amp;nbsp;loop&amp;nbsp;through&amp;nbsp;the&amp;nbsp;`itemList`&amp;nbsp;and&amp;nbsp;separate&amp;nbsp;the&amp;nbsp;items&amp;nbsp;based&amp;nbsp;on&amp;nbsp;whether&amp;nbsp;they&amp;nbsp;have&amp;nbsp;a&amp;nbsp;valid&amp;nbsp;`orderKey`&amp;nbsp;or&amp;nbsp;not.&amp;nbsp;If&amp;nbsp;the&amp;nbsp;`orderKey`&amp;nbsp;is&amp;nbsp;neither&amp;nbsp;null&amp;nbsp;nor&amp;nbsp;undefined,&amp;nbsp;we&amp;nbsp;push&amp;nbsp;the&amp;nbsp;item&amp;nbsp;into&amp;nbsp;`withTheData`;&amp;nbsp;otherwise,&amp;nbsp;we&amp;nbsp;push&amp;nbsp;it&amp;nbsp;into&amp;nbsp;`nullData`.&lt;br /&gt;&lt;br /&gt;After&amp;nbsp;that,&amp;nbsp;we&amp;nbsp;sort&amp;nbsp;both&amp;nbsp;arrays&amp;nbsp;using&amp;nbsp;a&amp;nbsp;custom&amp;nbsp;sorting&amp;nbsp;function&amp;nbsp;called&amp;nbsp;`sortFunction`.&amp;nbsp;This&amp;nbsp;function&amp;nbsp;allows&amp;nbsp;you&amp;nbsp;to&amp;nbsp;implement&amp;nbsp;your&amp;nbsp;specific&amp;nbsp;sorting&amp;nbsp;logic.&amp;nbsp;In&amp;nbsp;this&amp;nbsp;example,&amp;nbsp;we&amp;nbsp;sort&amp;nbsp;the&amp;nbsp;items&amp;nbsp;by&amp;nbsp;'Name',&amp;nbsp;assuming&amp;nbsp;'Name'&amp;nbsp;is&amp;nbsp;a&amp;nbsp;property&amp;nbsp;of&amp;nbsp;the&amp;nbsp;items.&lt;br /&gt;&lt;br /&gt;Finally,&amp;nbsp;the&amp;nbsp;function&amp;nbsp;returns&amp;nbsp;the&amp;nbsp;concatenated&amp;nbsp;result&amp;nbsp;of&amp;nbsp;`withTheData`&amp;nbsp;and&amp;nbsp;`nullData`,&amp;nbsp;ensuring&amp;nbsp;that&amp;nbsp;items&amp;nbsp;with&amp;nbsp;null&amp;nbsp;or&amp;nbsp;undefined&amp;nbsp;`orderKey`&amp;nbsp;appear&amp;nbsp;at&amp;nbsp;the&amp;nbsp;end&amp;nbsp;of&amp;nbsp;the&amp;nbsp;sorted&amp;nbsp;list.&lt;br /&gt;&lt;br /&gt;With this custom sorting function, you can now easily manage null or empty values in your AngularJS projects and get the exact sorting you want.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;Happy&amp;nbsp;coding&amp;nbsp;and&amp;nbsp;happy&amp;nbsp;sorting!&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;</description>
      <category>AngularJS</category>
      <category>angularjs</category>
      <category>handleNullData</category>
      <category>orderByFilter</category>
      <author>JoyJaewon</author>
      <guid isPermaLink="true">https://joyjaewon.tistory.com/11</guid>
      <comments>https://joyjaewon.tistory.com/11#entry11comment</comments>
      <pubDate>Wed, 26 Jul 2023 11:44:16 +0900</pubDate>
    </item>
    <item>
      <title>Introducing AngularJS and Analyzing the MVC Architecture</title>
      <link>https://joyjaewon.tistory.com/10</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Today, we will explore &lt;b&gt;AngularJS&lt;/b&gt;, a JavaScript MVC framework, and analyze its development pattern, the MVC (Model-View-Controller) structure. AngularJS has evolved since its inception, so let's take a look at its updated features and advantages.&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Introduction to AngularJS&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;AngularJS.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;1069&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qTMCU/btsgu7vJcg4/59EYwSmWaJLek8WbYetFIK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qTMCU/btsgu7vJcg4/59EYwSmWaJLek8WbYetFIK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qTMCU/btsgu7vJcg4/59EYwSmWaJLek8WbYetFIK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqTMCU%2Fbtsgu7vJcg4%2F59EYwSmWaJLek8WbYetFIK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;449&quot; height=&quot;300&quot; data-filename=&quot;AngularJS.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;1069&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;AngularJS is an opensource JavaScript framework that extends HTML for dynamic web application development. It provides a robust set of features and follows the MVC architectural pattern. With AngularJS, developers can build responsive and interactive web applications efficiently. Some key features of AngularJS include: &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1.&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt; Two-way data binding&lt;/b&gt;:&lt;/span&gt; AngularJS simplifies data synchronization between the model and view by automatically updating the view when the model changes, and vice versa. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2.&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;&amp;nbsp;Directives&lt;/b&gt;:&lt;/span&gt;&amp;nbsp;AngularJS&amp;nbsp;introduces&amp;nbsp;the&amp;nbsp;concept&amp;nbsp;of&amp;nbsp;directives,&amp;nbsp;which&amp;nbsp;allows&amp;nbsp;developers&amp;nbsp;to&amp;nbsp;extend&amp;nbsp;HTML&amp;nbsp;with&amp;nbsp;custom&amp;nbsp;attributes&amp;nbsp;and&amp;nbsp;elements.&amp;nbsp;Directives&amp;nbsp;enable&amp;nbsp;the&amp;nbsp;creation&amp;nbsp;of&amp;nbsp;reusable&amp;nbsp;components&amp;nbsp;and&amp;nbsp;enhance&amp;nbsp;the&amp;nbsp;declarative&amp;nbsp;nature&amp;nbsp;of&amp;nbsp;HTML. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;3.&amp;nbsp;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;SPA&amp;nbsp;(Single&amp;nbsp;Page&amp;nbsp;Application)&amp;nbsp;support&lt;/b&gt;:&lt;/span&gt;&amp;nbsp;AngularJS&amp;nbsp;facilitates&amp;nbsp;the&amp;nbsp;development&amp;nbsp;of&amp;nbsp;SPAs&amp;nbsp;by&amp;nbsp;offering&amp;nbsp;features&amp;nbsp;like&amp;nbsp;routing,&amp;nbsp;which&amp;nbsp;enables&amp;nbsp;seamless&amp;nbsp;navigation&amp;nbsp;between&amp;nbsp;different&amp;nbsp;views&amp;nbsp;without&amp;nbsp;page&amp;nbsp;reloads. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;4. &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Dependency injection&lt;/b&gt;:&lt;/span&gt; AngularJS provides a built-in dependency injection mechanism, allowing developers to manage and inject dependencies into components, promoting modularity and testability. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;5.&amp;nbsp;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Templating&lt;/b&gt;:&lt;/span&gt;&amp;nbsp;AngularJS&amp;nbsp;uses&amp;nbsp;HTML&amp;nbsp;as&amp;nbsp;a&amp;nbsp;template&amp;nbsp;language,&amp;nbsp;making&amp;nbsp;it&amp;nbsp;easier&amp;nbsp;to&amp;nbsp;create&amp;nbsp;dynamic&amp;nbsp;views&amp;nbsp;and&amp;nbsp;declaratively&amp;nbsp;bind&amp;nbsp;data&amp;nbsp;to&amp;nbsp;the&amp;nbsp;view. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;While&amp;nbsp;Angular&amp;nbsp;and&amp;nbsp;other&amp;nbsp;frameworks&amp;nbsp;like&amp;nbsp;React&amp;nbsp;and&amp;nbsp;Vue.js&amp;nbsp;have&amp;nbsp;gained&amp;nbsp;popularity&amp;nbsp;in&amp;nbsp;recent&amp;nbsp;years,&amp;nbsp;AngularJS&amp;nbsp;still&amp;nbsp;remains&amp;nbsp;in&amp;nbsp;many&amp;nbsp;legacy&amp;nbsp;codebases.&lt;b&gt;&amp;nbsp;It's&amp;nbsp;important&amp;nbsp;to&amp;nbsp;note&amp;nbsp;that&amp;nbsp;AngularJS&amp;nbsp;and&amp;nbsp;Angular&amp;nbsp;(also&amp;nbsp;known&amp;nbsp;as&amp;nbsp;Angular&amp;nbsp;2+)&amp;nbsp;are&amp;nbsp;distinct&amp;nbsp;frameworks&amp;nbsp;with&amp;nbsp;different&amp;nbsp;features&amp;nbsp;and&amp;nbsp;syntax. &lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;AngularJS MVC Structure and Execution Principles&lt;/span&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;740&quot; data-origin-height=&quot;569&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BwPsH/btsgvBpVvEP/zEz9itpcHtfTjUkxtXzGLK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BwPsH/btsgvBpVvEP/zEz9itpcHtfTjUkxtXzGLK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BwPsH/btsgvBpVvEP/zEz9itpcHtfTjUkxtXzGLK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBwPsH%2FbtsgvBpVvEP%2FzEz9itpcHtfTjUkxtXzGLK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;488&quot; height=&quot;375&quot; data-origin-width=&quot;740&quot; data-origin-height=&quot;569&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;The&amp;nbsp;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;MVC&amp;nbsp;architecture&amp;nbsp;&lt;/b&gt;&lt;/span&gt;plays&amp;nbsp;a&amp;nbsp;central&amp;nbsp;role&amp;nbsp;in&amp;nbsp;AngularJS&amp;nbsp;application&amp;nbsp;development. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1.&amp;nbsp;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;Model&amp;nbsp;(M):&lt;/b&gt;&lt;/span&gt;&amp;nbsp;The&amp;nbsp;model&amp;nbsp;represents&amp;nbsp;the&amp;nbsp;data&amp;nbsp;and&amp;nbsp;business&amp;nbsp;logic&amp;nbsp;of&amp;nbsp;the&amp;nbsp;application.&amp;nbsp;In&amp;nbsp;AngularJS,&amp;nbsp;the&amp;nbsp;model&amp;nbsp;is&amp;nbsp;often&amp;nbsp;defined&amp;nbsp;as&amp;nbsp;a&lt;b&gt;&amp;nbsp;JSON&lt;/b&gt;&amp;nbsp;structure,&amp;nbsp;which&amp;nbsp;can&amp;nbsp;be&amp;nbsp;accessed&amp;nbsp;and&amp;nbsp;manipulated&amp;nbsp;within&amp;nbsp;the&amp;nbsp;controllers.&amp;nbsp;To&amp;nbsp;bind&amp;nbsp;the&amp;nbsp;model&amp;nbsp;to&amp;nbsp;the&amp;nbsp;view,&amp;nbsp;AngularJS&amp;nbsp;uses&amp;nbsp;the&amp;nbsp;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;ng-model&amp;nbsp;&lt;/b&gt;&lt;/span&gt;directive. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2.&amp;nbsp;&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt;View&amp;nbsp;(V):&lt;/span&gt;&lt;/b&gt;&amp;nbsp;The&amp;nbsp;view&amp;nbsp;is&amp;nbsp;responsible&amp;nbsp;for&amp;nbsp;rendering&amp;nbsp;the&amp;nbsp;user&amp;nbsp;interface.&amp;nbsp;It&amp;nbsp;is&amp;nbsp;typically&amp;nbsp;created&amp;nbsp;using&amp;nbsp;HTML&amp;nbsp;templates&amp;nbsp;and&amp;nbsp;enhanced&amp;nbsp;with&amp;nbsp;AngularJS&amp;nbsp;directives&amp;nbsp;and&amp;nbsp;expressions.&amp;nbsp;The&amp;nbsp;view&amp;nbsp;interacts&amp;nbsp;with&amp;nbsp;the&amp;nbsp;model&amp;nbsp;through&amp;nbsp;data&amp;nbsp;binding,&amp;nbsp;displaying&amp;nbsp;and&amp;nbsp;updating&amp;nbsp;the&amp;nbsp;data&amp;nbsp;in&amp;nbsp;response&amp;nbsp;to&amp;nbsp;user&amp;nbsp;actions. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;3.&amp;nbsp;&lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;Controller&amp;nbsp;(C):&lt;/b&gt;&lt;/span&gt;&amp;nbsp;Controllers&amp;nbsp;handle&amp;nbsp;the&amp;nbsp;application's&amp;nbsp;logic&amp;nbsp;and&amp;nbsp;act&amp;nbsp;as&amp;nbsp;an&amp;nbsp;intermediary&amp;nbsp;between&amp;nbsp;the&amp;nbsp;model&amp;nbsp;and&amp;nbsp;the&amp;nbsp;view.&amp;nbsp;They&amp;nbsp;retrieve&amp;nbsp;data&amp;nbsp;from&amp;nbsp;the&amp;nbsp;model,&amp;nbsp;perform&amp;nbsp;necessary&amp;nbsp;operations,&amp;nbsp;and&amp;nbsp;update&amp;nbsp;the&amp;nbsp;model&amp;nbsp;accordingly.&amp;nbsp;In&amp;nbsp;AngularJS,&amp;nbsp;the&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&amp;nbsp;$scope&lt;/span&gt;&amp;nbsp;&lt;/b&gt;service&amp;nbsp;acts&amp;nbsp;as&amp;nbsp;the&amp;nbsp;bridge&amp;nbsp;between&amp;nbsp;the&amp;nbsp;controller&amp;nbsp;and&amp;nbsp;the&amp;nbsp;view,&amp;nbsp;allowing&amp;nbsp;easy&amp;nbsp;access&amp;nbsp;and&amp;nbsp;manipulation&amp;nbsp;of&amp;nbsp;data.&amp;nbsp;Controllers&amp;nbsp;are&amp;nbsp;defined&amp;nbsp;using&amp;nbsp;the&amp;nbsp;ng-controller&amp;nbsp;directive. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;The AngularJS execution model allows real-time updates and efficient data synchronization between the model and the view. Unlike traditional web applications, which require manual updates, AngularJS automatically reflects changes in the view as soon as the underlying data changes. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;In addition to the MVC structure, AngularJS provides routing capabilities. Similar to back-end routing in frameworks like Node.js, AngularJS enables front-end routing, allowing efficient navigation between views based on user interactions.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;AngularJS MVC Code Structure&amp;nbsp;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;Let's take a look at the basic code structure of an AngularJS application. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;740&quot; data-origin-height=&quot;404&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UK75S/btsgpYmOZV8/ymjgxXGNqQPJVs2Yf43pFk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UK75S/btsgpYmOZV8/ymjgxXGNqQPJVs2Yf43pFk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UK75S/btsgpYmOZV8/ymjgxXGNqQPJVs2Yf43pFk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUK75S%2FbtsgpYmOZV8%2FymjgxXGNqQPJVs2Yf43pFk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;505&quot; height=&quot;276&quot; data-origin-width=&quot;740&quot; data-origin-height=&quot;404&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;This&amp;nbsp;represents&amp;nbsp;the&amp;nbsp;fundamental&amp;nbsp;structure&amp;nbsp;of&amp;nbsp;an&amp;nbsp;AngularJS&amp;nbsp;application.&amp;nbsp;The&amp;nbsp;structure&amp;nbsp;can&amp;nbsp;evolve&amp;nbsp;and&amp;nbsp;become&amp;nbsp;more&amp;nbsp;complex&amp;nbsp;over&amp;nbsp;time.&amp;nbsp;In&amp;nbsp;this&amp;nbsp;post,&amp;nbsp;I&amp;nbsp;will&amp;nbsp;focus&amp;nbsp;on&amp;nbsp;explaining&amp;nbsp;how&amp;nbsp;the&amp;nbsp;MVC&amp;nbsp;structure&amp;nbsp;is&amp;nbsp;translated&amp;nbsp;into&amp;nbsp;code.&amp;nbsp;The&amp;nbsp;code&amp;nbsp;structure&amp;nbsp;of&amp;nbsp;AngularJS&amp;nbsp;follows&amp;nbsp;a&amp;nbsp;hierarchical&amp;nbsp;tree-like&amp;nbsp;organization.&amp;nbsp;The&amp;nbsp;top-level&amp;nbsp;attribute&amp;nbsp;is&amp;nbsp;ng-app,&amp;nbsp;which&amp;nbsp;refers&amp;nbsp;to&amp;nbsp;the&amp;nbsp;AngularJS&amp;nbsp;module&amp;nbsp;in&amp;nbsp;the&amp;nbsp;JavaScript&amp;nbsp;part&amp;nbsp;of&amp;nbsp;the&amp;nbsp;application.&amp;nbsp;In&amp;nbsp;most&amp;nbsp;AngularJS&amp;nbsp;projects, &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&amp;nbsp;the &lt;b&gt;ng-app&lt;/b&gt; attribute is typically set on the &lt;b&gt;&amp;lt;html&amp;gt;&lt;/b&gt; tag, indicating that the HTML file follows the AngularJS model. Controllers are implemented within&lt;b&gt; &amp;lt;div&amp;gt;&lt;/b&gt; elements, allowing the grouping of related functionality within a controller code block. &lt;b&gt;To define a controller, a callback function called&lt;span style=&quot;color: #ee2323;&quot;&gt; controller()&lt;/span&gt; is used&lt;/b&gt;. Multiple controllers can be created based on different functional areas. Finally, the binding between the controller and the view needs to be established. &lt;b&gt;The &lt;span style=&quot;color: #ee2323;&quot;&gt;$scope&lt;/span&gt; interface facilitates this binding, allowing the view to access the data (model) within the controller using $scope.modelName notation.&lt;/b&gt; When values are updated, the view is automatically refreshed. Each scope can access the data within its corresponding controller area. &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;In conclusion, we have explored the MVC structure of AngularJS. While other frameworks like Angular and React are more commonly used today, AngularJS still exists in many legacy codebases. In the next post, we will explore the&lt;b&gt;&lt;span style=&quot;color: #f89009;&quot;&gt; directives and {{expressions}}&lt;/span&gt;&lt;/b&gt;.&lt;/span&gt;&lt;/p&gt;</description>
      <category>AngularJS</category>
      <category>angularjs</category>
      <category>frontend</category>
      <author>JoyJaewon</author>
      <guid isPermaLink="true">https://joyjaewon.tistory.com/10</guid>
      <comments>https://joyjaewon.tistory.com/10#entry10comment</comments>
      <pubDate>Fri, 19 May 2023 03:12:31 +0900</pubDate>
    </item>
    <item>
      <title>My Desk Setup✨</title>
      <link>https://joyjaewon.tistory.com/9</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151;&quot;&gt;During the pandemic, remote work and online classes have become the new norm,&lt;span style=&quot;color: #374151;&quot;&gt;&amp;nbsp;resulting in us spending more time than ever at our desks&lt;/span&gt;. To make my work experience more productive and comfortable, I decided to upgrade my desk setup and have been using it ever since.&lt;/span&gt;&lt;span style=&quot;color: #374151;&quot;&gt; In this post, I'm excited to introduce you to my tech desk setup and share how it has helped me to enhance my productivity and comfort while working from home. So, Let's dive in!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;1. Dual Monitor&lt;/span&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_2349.jpg&quot; data-origin-width=&quot;2016&quot; data-origin-height=&quot;1512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xaNiY/btr6pcG23j4/JvMAOXMdzvpQBGJxKNcK6K/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xaNiY/btr6pcG23j4/JvMAOXMdzvpQBGJxKNcK6K/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xaNiY/btr6pcG23j4/JvMAOXMdzvpQBGJxKNcK6K/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxaNiY%2Fbtr6pcG23j4%2FJvMAOXMdzvpQBGJxKNcK6K%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;532&quot; height=&quot;399&quot; data-filename=&quot;IMG_2349.jpg&quot; data-origin-width=&quot;2016&quot; data-origin-height=&quot;1512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151;&quot;&gt;One of the best investments I made in my tech desk setup was adding a second monitor. The dual monitor setup has improved my productivity and made multitasking a breeze. With the ability to work on two screens simultaneously, I can easily reference multiple documents or applications without having to switch back and forth between them.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151;&quot;&gt;My first monitor is LG UltraFine UHD 27-inch 4K UHD and the second monitor is Dell 24-inch FHD.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;2. Standing Desk&lt;/span&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;ezgif.com-video-to-gif (3).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;338&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bfduhj/btr6tY85WYJ/xstyC0vnCQWh51TgJKivA1/img.gif&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bfduhj/btr6tY85WYJ/xstyC0vnCQWh51TgJKivA1/img.gif&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bfduhj/btr6tY85WYJ/xstyC0vnCQWh51TgJKivA1/img.gif&quot; srcset=&quot;https://blog.kakaocdn.net/dn/bfduhj/btr6tY85WYJ/xstyC0vnCQWh51TgJKivA1/img.gif&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;530&quot; height=&quot;299&quot; data-filename=&quot;ezgif.com-video-to-gif (3).gif&quot; data-origin-width=&quot;600&quot; data-origin-height=&quot;338&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151;&quot;&gt;One of the most significant changes I made to my desk setup was purchasing a standing desk. I had been contemplating buying one for a long time and finally decided to take the plunge when I found a great deal on Amazon Prime Day. It turned out to be a life-changer! The standing desk has helped me to reduce back pain and improve my posture. I no longer feel sluggish or tired after sitting for long hours at my desk. Instead, I can switch between sitting and standing positions throughout the day, which has helped me to stay more alert and focused!&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;3. Leopold 660C + Arm Rest&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_8216.jpg&quot; data-origin-width=&quot;2016&quot; data-origin-height=&quot;1512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cS0QqF/btr53EyAVQq/sv286sO8Wo1CZGJT35Wbj1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cS0QqF/btr53EyAVQq/sv286sO8Wo1CZGJT35Wbj1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cS0QqF/btr53EyAVQq/sv286sO8Wo1CZGJT35Wbj1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcS0QqF%2Fbtr53EyAVQq%2Fsv286sO8Wo1CZGJT35Wbj1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;530&quot; height=&quot;398&quot; data-filename=&quot;IMG_8216.jpg&quot; data-origin-width=&quot;2016&quot; data-origin-height=&quot;1512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151;&quot;&gt;&lt;span style=&quot;color: #374151;&quot;&gt;As someone who spends a lot of time typing, I wanted a keyboard that would be both comfortable and produce satisfying typing sound. After researching various options, I decided on the Leopold 660C mechanical keyboard with Topre switches.&lt;/span&gt; I also added an wrist rest to provide extra support for my wrists and arms. &lt;/span&gt;&lt;span style=&quot;color: #374151; letter-spacing: 0px;&quot;&gt;It has significantly improved my comfort during long typing sessions, and I highly recommend getting the wrist rest if you haven't used it before.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;4. Vertical Laptop Stand&lt;/span&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;IMG_8217.jpg&quot; data-origin-width=&quot;2016&quot; data-origin-height=&quot;1512&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/k3iyl/btr6qPSgDnC/BXXrMZjiKI7JqGMRaGKhnK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/k3iyl/btr6qPSgDnC/BXXrMZjiKI7JqGMRaGKhnK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/k3iyl/btr6qPSgDnC/BXXrMZjiKI7JqGMRaGKhnK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fk3iyl%2Fbtr6qPSgDnC%2FBXXrMZjiKI7JqGMRaGKhnK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;529&quot; height=&quot;397&quot; data-filename=&quot;IMG_8217.jpg&quot; data-origin-width=&quot;2016&quot; data-origin-height=&quot;1512&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151;&quot;&gt;To save space and improve the design of my desk setup, I invested in a vertical laptop stand for my MacBook. The stand allows me to close my MacBook and connect it to my external monitor, which frees up space on my desk and provides a clean, minimalist look. &lt;span style=&quot;color: #374151;&quot;&gt;I even have some hidden space behind the stand, which means that even if I make a mess behind it, no one will know &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Having the ultimate desk setup is crucial for productivity and comfort while working from home.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;This desktop has made a significant impact on my work experience during these times of remote work and online learning. If you have any questions about my setup, please let me know. Have a wonderful day&lt;span&gt;✨&lt;/span&gt;&lt;/p&gt;</description>
      <category>Devlog</category>
      <category>Desk Setup</category>
      <author>JoyJaewon</author>
      <guid isPermaLink="true">https://joyjaewon.tistory.com/9</guid>
      <comments>https://joyjaewon.tistory.com/9#entry9comment</comments>
      <pubDate>Tue, 28 Mar 2023 05:18:11 +0900</pubDate>
    </item>
    <item>
      <title>Client-Side Rendering(CSR) vs Server-Side Rendering(SSR)</title>
      <link>https://joyjaewon.tistory.com/8</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;When learning React, one of the terms you will often encounter is client-side rendering. As I'm currently studying next.js, I keep coming across the term server-side rendering, so I decided to write a blog post to better understand and clarify these concepts.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;At a glance, &lt;b&gt;server-side rendering (SSR)&lt;/b&gt; and &lt;b&gt;client-side rendering (CSR)&lt;/b&gt; seems to indicate whether the rendering takes place on the server or client side, but what exactly is the difference between the two? First, let's define &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;rendering&lt;/span&gt;&lt;/b&gt;: it refers to &lt;span style=&quot;color: #f89009;&quot;&gt;&lt;b&gt;the process of displaying content received from a server on a browser screen&lt;/b&gt;&lt;/span&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;The difference between SSR and CSR lies in where and how the content is rendered. CSR renders the content in the browser, while SSR fully renders the content on the server and sends it to the browser. &lt;/span&gt;Now that we understand what CSR and SSR are, let's take a closer look at how they work and their advantages and disadvantages.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;1) Client-Side Rendering (CSR)&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-03-15 오후 4.27.31.png&quot; data-origin-width=&quot;1232&quot; data-origin-height=&quot;934&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bPGTq7/btr34BC7p7T/VJzKKrkZvShu8aeB5GKt8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bPGTq7/btr34BC7p7T/VJzKKrkZvShu8aeB5GKt8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bPGTq7/btr34BC7p7T/VJzKKrkZvShu8aeB5GKt8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbPGTq7%2Fbtr34BC7p7T%2FVJzKKrkZvShu8aeB5GKt8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;516&quot; height=&quot;391&quot; data-filename=&quot;스크린샷 2023-03-15 오후 4.27.31.png&quot; data-origin-width=&quot;1232&quot; data-origin-height=&quot;934&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;CSR became popular alongside the rise of the &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;SPA&lt;/span&gt; &lt;/b&gt;trend, increased CPU performance, and the standardization of JavaScript frameworks (such as React, Vue, and Angular). In a nutshell, CSR involves processing everything on the client side. The server renders the entire page once, and the client then interprets and renders any additional resources requested by the user. Unlike SSR, CSR does not involve requesting HTML documents from the server but rather rendering content in the browser using JavaScript.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d; text-align: start;&quot;&gt;*SPA (Single Page Application): an application that loads an entire page once and then only changes the data for subsequent use.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-03-15 오후 4.22.34.png&quot; data-origin-width=&quot;1034&quot; data-origin-height=&quot;484&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJVSab/btr38N3lbmD/wtRiMbfSJzrVcGoKZXi09K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJVSab/btr38N3lbmD/wtRiMbfSJzrVcGoKZXi09K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJVSab/btr38N3lbmD/wtRiMbfSJzrVcGoKZXi09K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJVSab%2Fbtr38N3lbmD%2FwtRiMbfSJzrVcGoKZXi09K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;553&quot; height=&quot;259&quot; data-filename=&quot;스크린샷 2023-03-15 오후 4.22.34.png&quot; data-origin-width=&quot;1034&quot; data-origin-height=&quot;484&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;A simple example of CSR involves an HTML body containing only a single element with an id of &quot;root&quot; and links to the necessary JavaScript files. The HTML is almost entirely empty, so when users first visit the page, they see a blank screen while the linked JavaScript files are downloaded. These files contain the logic, framework, and libraries necessary for the application to run, which can take some time to download initially. When additional data is needed, it is fetched from the server and dynamically rendered on the client side, along with the JavaScript, before being shown to the user.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The main disadvantages of CSR are 1) it can take a long time for users to see the initial content and 2) poor &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;SEO (Search Engine Optimization)&lt;/b&gt;&lt;/span&gt; performance.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;*What is &lt;b&gt;SEO&lt;/b&gt;?&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #9d9d9d;&quot;&gt;Search engines like Google crawl registered websites and analyze their HTML documents to help users quickly find the desired content. However, since the HTML body in CSR is mostly empty, search engines struggle to analyze websites built using CSR.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;These issues have led to the introduction of &lt;b&gt;server-side rendering (SSR)&lt;/b&gt;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;2) Server-Side Rendering&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;In SSR, instead of handling everything on the client side, the server fetches all necessary data and creates an HTML file when a user visits a website. This HTML file and the source code needed for some dynamic control are sent to the client. The client then displays the fully rendered HTML document to the user.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;As a result, SSR has the advantages of faster page loading and efficient SEO, as all content is included in the HTML. &lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;In SSR, the drawbacks of CSR, such as 1) slow page loading and 2) poor SEO performance, are addressed.&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;However, SSR does have its own disadvantages:&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;&lt;span style=&quot;text-align: start;&quot;&gt;1. Blinking Issue&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;&lt;span style=&quot;text-align: start;&quot;&gt;2. Server Overload&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;&lt;span style=&quot;text-align: start;&quot;&gt;3. Gap between TTV and TTI&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;First, the &lt;b&gt;Blinking Issue&lt;/b&gt; occurs when users refresh the page and the entire website has to be fetched from the server again, causing the screen to momentarily disappear and reappear. This is not ideal from a UX perspective.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Second, SSR can lead to &lt;b&gt;server overload&lt;/b&gt;, as more users result in more requests for data from the server.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;The third &lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;and most critical disadvantage is the &lt;b&gt;gap between Time To View (TTV) and Time To Interact (TTI)&lt;/b&gt;. To understand this, we need to first know what TTV and TTI are.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-03-15 오후 3.51.03.png&quot; data-origin-width=&quot;1320&quot; data-origin-height=&quot;940&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/KrCQQ/btr4bks2ylj/AvOs32w4dASSZ8IayKG7jk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/KrCQQ/btr4bks2ylj/AvOs32w4dASSZ8IayKG7jk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/KrCQQ/btr4bks2ylj/AvOs32w4dASSZ8IayKG7jk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FKrCQQ%2Fbtr4bks2ylj%2FAvOs32w4dASSZ8IayKG7jk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;526&quot; height=&quot;375&quot; data-filename=&quot;스크린샷 2023-03-15 오후 3.51.03.png&quot; data-origin-width=&quot;1320&quot; data-origin-height=&quot;940&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;In SSR, the server-generated HTML file is sent to the client, allowing users to view the website immediately. However, since the JavaScript files needed for dynamic control have not yet been received, users cannot interact with the website even if they click on elements. Only after the JavaScript files are received can users interact with the website as intended. &lt;b&gt;As a result, SSR has a significant gap between the time users can view the site (TTV) and when they can actually interact with it (TTI).&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;On the other hand, in CSR, nothing is displayed until the HTML and all linked JavaScript files have been received. When the website becomes visible, users can interact with it immediately, eliminating any gap between TTV and TTI.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;In conclusion, both SSR and CSR have their own advantages and disadvantages. SSR provides faster page loading and better SEO, but it suffers from issues such as the Blinking Issue, server overload, and a gap between TTV and TTI. Meanwhile, CSR offers seamless interactivity with no gap between TTV and TTI, but it can result in slower initial loading times and poor SEO performance. When choosing between SSR and CSR, it's essential to consider the specific requirements and goals of your project to determine the best rendering approach.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;Reference&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;Youtube- Dream Coding&quot; href=&quot;https://www.youtube.com/watch?v=iZ9csAfU5Os&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;Youtube-Dream Coding&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;Youtube- Code Factory&quot; href=&quot;https://www.youtube.com/watch?v=5W72UHb-9iI&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;Youtube-Code Factory&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a title=&quot;Vue SSR Guide&quot; href=&quot;https://vuejs.org/guide/scaling-up/ssr.html#code-structure&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;color: #374151; text-align: start;&quot;&gt;Vue SSR Guide&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>React</category>
      <category>CSR</category>
      <category>Next.js</category>
      <category>React.js</category>
      <category>SSR</category>
      <author>JoyJaewon</author>
      <guid isPermaLink="true">https://joyjaewon.tistory.com/8</guid>
      <comments>https://joyjaewon.tistory.com/8#entry8comment</comments>
      <pubDate>Thu, 16 Mar 2023 06:30:46 +0900</pubDate>
    </item>
  </channel>
</rss>