hmos.dev Engineering Blog

website·Indexed 32 days ago

https://hmos.dev/

A technical blog by front-end engineer Hyunmo Ahn focusing on web development, featuring deep dives into Immer, React performance optimization, Next.js Docker configurations, i18next with TypeScript, and Open API Specification (OAS) generation.

Tokens
56.3K
Snippets
218
Records
308
Agent score
52%

What's inside hmos.dev

  1. Overview of OAS Generator

    The OAS Generator is a tool that automates the creation of source code (such as TypeScript, Java, or Kotlin) based on an Open API Specification (OAS) YAML or JSON file. It transforms the API definition flow from: API Swagger $\rightarrow$ OAS text file (.yaml) $\rightarrow$ Source Code (.ts). In the web frontend ecosystem, it is commonly used to generate clients for typescript-axios or typescript-fetch.
  2. Overview of Dockerizing Next.js applications

    Dockerizing a Next.js application can vary in complexity depending on whether the project uses Standalone mode or a Custom Server. There are four primary architectural patterns for deploying Next.js in Docker: a basic build/run, using standalone mode for optimized production builds, implementing a custom server for specialized routing/logic, or combining both standalone mode and a custom server.
    CaseDescriptionStandaloneCustom Server
    1Basic Next.js build and run❌❌
    2Running in standalone mode✅❌
    3Using a custom server❌✅
    4Standalone + Custom Server✅✅
  3. Overview of the OAS Generator workflow

    The OAS Generator is a tool that automates the creation of TypeScript development code from API documentation. It reduces human error and the manual effort required to create types by following a specific generation flow: a YAML file is processed by the oas-generator-cli to produce the final generated code. The tool supports custom code templates to fit specific project requirements.
  4. Introduction to Immer for immutable state management

    Immer is a library that allows developers to work with immutable state in a more convenient way. In JavaScript, non-primitive types (objects, arrays) are mutable, meaning their internal values can change without changing their reference. This is problematic for tools like Redux that use shallow equality checking to detect state changes. Immer ensures that data is updated immutably, guaranteeing a new reference whenever the state is modified, which avoids the performance cost of deep equality checking.
  5. Understand Next.js Dockerization strategies for Standalone and Custom Servers

    When dockerizing a Next.js project, there are four primary deployment configurations based on whether the project uses the standalone output mode (which automatically copies traced files for a smaller build) and whether it utilizes a custom server. Choosing the right combination is critical for successful containerization.
    CaseDescriptionStandaloneCustom Server
    1Basic Next.js build and execution❌❌
    2Execution in Standalone mode✅❌
    3Using a Custom Server❌✅
    4Standalone + Custom Server✅✅
  6. Implement strong type inference for i18next translation functions

    When using i18next with TypeScript, a standard translation function (tFunction) often lacks the precision to distinguish between a simple string return and a ReactElement return. To prevent runtime bugs (such as [object Object] appearing in HTML attributes like placeholder), the return type should be inferred based on two criteria:

    1. Content Patterns: If the translation string contains a line break character (\n), the function should return a ReactElement (since \n is typically converted to <br /> in React).
    2. Argument Types: If the variables passed to fill placeholders (e.g., {0}) include JSX elements (like <a> tags), the function should return a ReactElement regardless of the string content.

    This can be achieved using TypeScript 4.1+ Template Literal Types to analyze the i18n JSON keys and values at the type level.

  7. Understand i18n return type challenges in React with TypeScript

    When using i18n functions (like i18next.t or a custom tFunction), the return type can vary between string and ReactElement based on the content of the translation key and the values passed to it. Specifically, if a translation contains newline characters (\n) or if JSX elements are passed as interpolation values, the result must be a ReactElement to be rendered correctly in React. If the type is not accurately inferred, passing an i18n result to a string-only attribute (like an HTML <input> placeholder) can result in [object Object] being displayed instead of the intended text.
    // Returns string
    tFunction('simple-example') 
    
    // Returns ReactElement because of \n
    tFunction('line-break-example') 
    
    // Returns ReactElement because a JSX element is passed as a value
    tFunction('values-example', [<a href="/about">more</a>])
  8. Dockerize Next.js applications using Standalone mode and Custom Servers

    Dockerizing Next.js applications can vary depending on whether you use 'Standalone' mode (which automatically copies traced files for production) or a 'Custom Server'. There are four primary architectural patterns for Dockerizing Next.js based on these configurations: basic build/run, standalone mode only, custom server only, or a combination of both standalone mode and a custom server.
    CaseDescriptionStandaloneCustom Server
    1Basic Next.js build and run❌❌
    2Running in standalone mode✅❌
    3Using a custom server❌✅
    4Standalone + Custom Server✅✅
  9. Understand how Immer achieves immutability using Proxies

    Immer maintains immutability by wrapping the base state in a Proxy object. It intercepts basic object operations (like get and set) to prevent direct modification of the original data.

    Key internal mechanisms include:

    • base_: Stores the original data.
    • copy_: Stores the updated data (a shallow copy created when a change is first detected).
    • modified_ flag: A boolean tracker that indicates whether a specific object or property has been changed.
    • Recursive Proxying: The get trap ensures that any nested object accessed is also returned as a Proxy, allowing Immer to track changes deep within the object tree.
  10. Understand Redux-DevTools core functionality for custom implementation

    Redux-DevTools allows developers to record Redux state information (actions, reducers, and state), rollback to specific reducer states, and skip specific actions to simulate them never having occurred. Implementing these 'time-travel' features directly within a web application's business logic (e.g., rolling back all actions on page exit or undoing a specific button click) is complex. The core mechanism of Redux-DevTools relies on Redux enhancers to achieve logging, jumping to specific dispatch points, and skipping actions.
  11. Understand how Immer implements immutable updates via Proxies

    Immer allows developers to write mutable code to update state while maintaining immutability. It achieves this by wrapping the base state in a Proxy object.

    Key internal mechanisms include:

    • Interception: Immer intercepts get and set operations on the state.
    • Proxy Chain: When accessing nested objects, Immer recursively creates proxies for every object from the root to the target, ensuring the same proxy logic applies at all depths.
    • Copy-on-Write: When a set operation occurs, Immer shallowly copies the original data (base_) to a new object (copy_) and applies the change there.
    • Modification Tracking: A modified_ flag is used to track if an object has been changed. If a child object is modified, the modified_ flag is set to true for that object and all its parents up to the root.
  12. Understand the redux-devtools internal architecture

    Redux-devtools implements time-travel debugging using the following architectural patterns:

    • Store Connection: It connects to the application's Redux store via a store enhancer.
    • Store Lifting: To avoid interfering with the application's primary logic, it manages two stores: unliftStore and liftStore.
    • Action Wrapping: All application actions are converted into liftAction objects with a PERFORM_ACTION type and sent to a liftReducer.
    • State Management: The liftReducer records every action and the resulting state. The application's actual state is derived by calling getState on the unliftStore for the state corresponding to the currently selected action index.
    • Time Travel: Features like 'jump' and 'skip' work by modifying the stored actions/states in the liftReducer and delivering the resulting state of the desired point in time back to the application.