[카테고리:] Javascript

  • Next.js 에서 loading 페이지나 스트리밍 기능 사용 시 404 처리가 불가능합니다

    현재 Next.js 15.1.7 버전으로 작업 중인데요. dynamic route로 API 호출해 처리하는 페이지가 있고 페이지 호출 시 id 값으로 API 호출하는데 자료가 없는 경우 HTTP STATUS로 404 리턴을 해줘야 합니다.

    일반적으로는 아래와 같은 코드로 작동할 수 있습니다.
    예를 들어 app/pages/[id]/page.tsx 파일을 아래와 같이 작성합니다.

    import { notFound } from 'next/navigation';
    
    export default async function Page(props: {
      params: Promise<{ id: number }>;
    }) {
      const pparams = await props.params;
      const id = Number(pparams.id);
      const data = await fetch(API_URI, { localdata_id });
      if (!data) {
        notFound();
      }
      return (<div>{JSON.stringify(data)}</div>)
    }
    

    /pages/112 이런 형태로 호출하면 정상적으로 200 리턴하며 페이지를 출력할 것입니다.

    만약 999999 란 아이디가 없다면 /pages/999999 호출 시 notFound() 가 호출됩니다.

    notFound() 의 return 형식은 never 입니다. 절대로 리턴이 없다는 의미이며 내부적으로 예외를 발생시키는 것으로 알고 있습니다.

    결국 Next.js 에서 처리해서 정상적으로 404 리턴을 해줍니다.

    문제점

    하지만 아래와 같이 loading.tsx 를 넣어서 로딩 UI 나 스트리밍 처리를 넣게 되면 무조건 200 리턴만 가능합니다.

    app/loading.tsx 를 아래와 같이 넣는다면…

    'use client';
    import CircularProgress from '@mui/material/CircularProgress';
    import { useEffect, useState } from 'react';
    export default function Loading() {
      const [showLoading, setShowLoading] = useState(false);
      useEffect(() => {
        const timer = setTimeout(() => {
          setShowLoading(true);
        }, 500); // 0.5초 후에 로딩 페이지를 보여줌
        return () => clearTimeout(timer); // 컴포넌트가 unmount 될 때 타이머 정리
      }, []);
      // 0.5초 이전에는 아무것도 렌더링 하지 않음
      if (!showLoading) {
        return null;
      }
      // You can add any UI inside Loading, including a Skeleton.
      return (
        <div className="page-content w-desktop flex flex-col items-center justify-center">
          <CircularProgress />
          <div>Loading...</div>
        </div>
      );
    }
    

    Next.js는 loading.tsx 파일 있는 경우 페이지 스트리밍 처리를 자동으로 시작하므로 무조건 200 리턴을 하고 페이지 로딩을 이어가기 때문에 중간에 notFound() 호출은 이미 버스가 떠난 뒤에 손을 흔드는 것과 같습니다.

    따라서 loading UI 를 사용하면서도 404 처리를 하려면 middleware를 활용하는 수밖에 없습니다. 문제는 이때 미들웨어에서 먼저 API 를 fetch() 한 결과를 React.cache()로 캐싱 처리해도 React.cache() 는 서버 컴포넌트 내에서만 캐시 처리가 가능하기 때문에 Next.js 에서 제공하는 미들웨어서는 캐싱이 작동하지 않아 페이지 한 번 호출에 여러번 API 를 호출하게 됩니다. fetch() 옵션으로 캐싱을 넣을 수도 있지만 미들웨어에서 호출하는 것과 서버 컴포넌트에서 호출하는 것이 실제로는 헤더부분이 달라 다른 것으로 인식해 간단히 캐싱 처리가 되지 않습니다.

    결국 Next.js 가 제공하는 기능인 loading.tsx를 제거하고 로딩 처리는 자체적으로 처리하는 형태로 변경해야 정상적으로 notFound() 가 작동해 404 처리가 가능해집니다.

    참고자료

  • STRAPI + MySQL를 docker compose로 띄우는 도중 생기는 오류 해결하기

    STRAPI + MySQL를 docker compose로 띄우는 도중 생기는 오류 해결하기

    STRAPI를 사용해 보려고 기본 세팅으로 시작해보니 sqlite를 기본 디비로 사용하고 있더군요. 간단한 기능은 이 디비로 가능하지만 제대로 하려면 역시 최소한 MySQL (MariaDB) 를 사용해야 합니다.

    (더 보기…)
  • [링크] Vue 및 Nuxt 사용시 성능향상을 위해 읽어볼 만한 글들

    1. How we got a 100% Lighthouse performance score for our Vue.js app

    라이트하우스는 사이트의 전체적인 성능을 확인해주는 툴중 하나인데요. 크롬 확장으로 설치헤서 테스트를 원하는 페이지에서 바로 성능 및 기타 SEO 친화도등을 검사해 볼 수 있습니다.

    이런 라이트하우스 100% 나오는 뷰앱이라니.. 흥미가 생기네요.

    https://vuejsdevelopers.com/2017/07/03/vue-js-code-splitting-webpack/

    1번에 내용에 보다보면 코드분할에 대한 내용이 나오면서 위 글을 추천하네요. 이것도 읽어보면 도움이 될 거 같습니다.

  • [링크] FCM 으로 구현하는 채팅 웹앱 예제

    구글링하다 찾았는데요. 잊어버릴까봐 링크로 올려둡니다.

    안드로이드 모바일앱에서 많이 사용하던 GCM 을 뒤로하고 앞으로 모바일앱만이 아닌 웹브라우저 등 여러 기기에서 범용적으로 사용할 수 있게 만든 FCM (Firebase Cloud Messaging) 을 이용해 채팅 웹앱을 만들어보는 멋진 강좌입니다.

    https://cionman.tistory.com/51

  • TinyMCE 5.x 이상부터 모바일 UI 가 바뀌는 문제 해결하기

    TinyMCE 를 5.x 로 업그레이드하고 나서 생기는 문제입니다. 버전 5 부터는 디자인도 좀 더 세련되게 바뀌고 전체적으로 UI 가 깔끔해졌는데요. 예기치 못한 복병이 있었네요.

    PC 상에서는 큰 문제가 없는데 모바일에서 보면 바로 편집할 수 있는 창이 뜨지않고 아래와 같이 덩그러니 아이콘 하나만 나옵니다.

    (더 보기…)
  • 경고 해결 – [Deprecation] Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user’s experience. For more help, check https://xhr.spec.whatwg.org/.

    jQuery 자바스크립트 작업을 하다보면 다음과 같은 경고를 만나는 경우가 있다.

    [Deprecation] Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user's experience. For more help, check https://xhr.spec.whatwg.org/.

    위 경고는 아래의 코드를 실행했을때 생겼다.

    jQuery(function() {
      for (var i = 0; i < 10; ++i) {
        $.ajax({
          url: 'test-url',
          async: false,
          success: function(data) {
            ...
          }
        })
      }
    })
    

    경고가 발생하는 것은 jQuery 가 1.8 버전부터 async 옵션이 폐기(deprecation)되었다. 대신에 jqXHR.done(), jqXHR.fail(), jqXHR.always() 을 사용할 수 있다.

    jQuery 로 페이지 로딩시 ajax 처리를 하는데 동기적으로 처리해야 되는 상황에서 발생했다.

    위와 같은 문제가 발생하는 가장 근본적인 원인은 ajax 를 반복문으로 호출하기 때문이다. 따라서 가장 좋은 해결방법은 ajax 를 한번만 호출하도록 변경하는게 최선이다.

    하지만 서버측을 변경할 수 없는 환경에서는 사용할 수 없는 방법이다. 차선책으로 이를 해결하기 위해서는 Promise 기법을 사용해야 한다.

    jQuery(function() {
      function loadAjax(i, limit) {
        $.ajax({
          url: 'test-url'
        })
        .done(function(data) {
          // success 옵션의 함수와 동일
            ... 
          if (i < limit) {
            loadAjax(i + 1, limit)
          }
        })
        .fail(function(){
          // ajax 호출이 실패하는 경우
        })
      }
    
      loadAjax(0, 10) // 재귀호출시작!
    })

    코드는 그럭저럭이지만 재귀적 호출이 되버려서 사실상 콜백지옥인 상황은 동일하다 ㅡ.ㅡ; limit 이 얼마 안된다면 상관없지만 매우 큰수라면 사용하기 힘들것이다.

    jQuery.ajax() 공식문서 바로가기 : https://api.jquery.com/jquery.ajax/

  • 알아두면 편리한 정규표현식 모음 (계속 수정 예정)

    정규 표현식은 참 손에 안잡히는 주제중 하나죠. 이걸 꿰차면 상당히 프로그래밍 할때 막히는 부분이 줄어들겠지만 쉽게 이해 되지 않는 것도 사실입니다. 그래서 각 경우에 따른 표현식 모음을 작성하기로 해봤는데요. 앞으로도 작업하다가 마주치는 정규 표현식을 적어둘 예정입니다.

    1. 주소에서 맨 끝에 괄호 추가정보 제거하기

    네이버지도 API가 이전 및 유료화 되면서 대안으로 다음 지도 API 를 사용하면서 생긴 문제입니다.
    현재 주소를 저장할 때 1차, 2차로 나눠서 1차는 도로명주소로 저장하는데요. 정식 도로명 주소가 아직은 익숙하지 않기 때문에 맨뒤에 괄호'(…)’로 구분에 동이름이나 건물명등을 넣어 보완하고 있습니다.

    이렇게 저장한 1차 주소를 이용해 카카오지도로 연결하면 문제가 발생하는 경우가 있습니다. 웹버전의 카카오지도에서는 크게 문제 없이 바로 지도가 나오는데요. 모바일 카카오지도앱의 경우 정식 도로명주소 뒤에 추가정보가 붙으면 그냥 지도를 보여주지 않고 주소 선택하는 페이지가 먼저 뜹니다. 이게 은근히 불편하더라고요. 그래서 바로 지도를 보여줄 수 있도록 하기 위해서 맨뒤에 괄호 추가 정보를 제거하는 정규 표현식 입니다.

    // 예제 주소 문자열 : "제주특별자치도 제주시 첨단로 242 (영평동)"
    // 예상 결과 : "제주특별자치도 제주시 첨단로"
    
    // Javascript
    "제주특별자치도 제주시 첨단로 242 (영평동)".replace(/\s+\(.+\)$/g, '')
    
    // 주소 맨끝에 빈칸으로 구분한 괄호만을 찾기 위해 : \s+ 
    // 빈칸을 \s 로 표현했다. 뒤에 + 는 앞의 문자가 1개나 그이상 오는 것을 표현한다. 사실 이부분은 그다지 필요없는 부분이다.
    // 괄호 및 괄호내 내용 : \(.+\)
    // 괄호는 이스케이프 문자로 표현하고 괄호 안의 내용은 .+ 로 표현했다. 점(.)은 모든 문자 하나를 표현하고 + 는 앞 문자의 1개나 그이상의 문자를 표현한다.
    // 주소 맨끝의 괄호내용만 찾기위한 $
    // 문자열 전체 적용을 위한 플래그 g
    

    테스트하기 : https://regex101.com/r/9G7rLQ/3

    정규 표현식 관련 알기 쉽게 설명이 나온 글입니다 참조하시기 바랍니다 : https://medium.com/@originerd/정규표현식-좀-더-깊이-알아보기-5bd16027e1e0

    정규표현식 테스트 및 분석하기 좋은 사이트 : https://regex101.com/

  • axios 사용시 폼 데이터 전송하기 (+파일 업로드)

    axios 사용시 폼 데이터 전송하기 (+파일 업로드)

    axios 의 post 기능은 기본적으로 폼 데이터 전송방식을 사용하지 않기 때문에 서버쪽에서 파라메터를 받는 부분을 수정할 수 없는 상황이라면 문제가 됩니다. 보통 외부 API 서비스를 사용할 때 많이 발생하지요.

    // 보통 axios 는 아래와 같이 보내게 된다. 
    // 이렇게 보내면 폼 전송형식이 아닌 JSON 포맷으로 전송하게 된다.
    axios.post('https://domain/form-post-url', {
      name: '이름'
      key: '값'
    }).then((response) => {
      // 응답 처리
    })
    .catch((error) => {
      // 예외 처리
    })
    (더 보기…)
  • 알쏭달쏭 Nuxt.js 에서 앵커 링크 처리하기

    Nuxt.js 는 여러모로 기본 Vue 하고도 다르고 SPA (Single Page Application) 에 알맞기 때문에 기존의 일반적인 페이지 처리와 다른 부분들이 있다.

    특히 앵커에 대해서 처리가 많이 다른데…

    (더 보기…)