문제 배경

개선

상태관리의 복잡성을 떠나 문제의 본질은 이전 그래픽 요소가 제대로 제거되지 않아 생긴 것으로, Canvas API의 clearRect() 메서드를 활용해 캔버스를 초기화하는 방식으로 해결을 시도하였다. 즉, 다음과 같은 과정을 통해 문제를 해결하였다.

  1. 초기화 함수 구현

    clearRect(0, 0, canvas.width, canvas.height)를 통해 Canvas 전체를 초기화하는 clearCanvas 함수를 정의하였다.

        const clearCanvas = (ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement) => {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
        };
    
  2. 호출 위치 탐색

    단순히 draw() 함수가 호출되기 직전에 초기화 함수를 넣는 것으로는 충분하지 않았다. draw()는 여러 번 호출되며 이미지가 비동기적으로 로드되기 때문에, 잔상이 여전히 남는 문제가 있었다.

  3. 최적의 호출 타이밍 조정

    최종적으로 imageElload 이벤트 내부에서 clearCanvas를 실행하여 이미지가 완전히 로드된 후 캔버스를 초기화하고, 이후에 drawImage()fillText()가 실행되도록 로직을 변경하였다. 이로 인해 그래픽 요소의 렌더링 순서와 타이밍을 명확히 통제할 수 있었다.

    [참고] 개선된 전체 코드

    // Reset | 캔버스 초기화 함수 정의
    const clearCanvas = ( ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement ) => {
        ctx.clearRect(0, 0, canvas.width, canvas.height)
      }
      
      // === 중략 ===
      
      // 이미지 로드 이벤트 핸들
      const loadImage = useCallback((textY: number, ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement, split: string[]) => {
        if (!imageEl) return
        imageEl.alt = '명언 카드 배경 이미지'
        imageEl.addEventListener('load', () => {
        
         // Reset | 이미지가 로드된 후 캔버스 초기화 함수 호출
          clearCanvas(ctx, canvas) 
          
          bgColorDraw(ctx, width, height)
          ctx.fillStyle = `${color}`
          ctx.drawImage(imageEl, 0, 0, canvas.width, canvas.height)
    
          // 배열 형태로 분리된 텍스트를 조건에 따라서 다르게 렌더링한다.
          split.forEach((text: string, i: number) => { // === 중략 === })
        })
      },[bgColorDraw, color, fontStyle, height, imageEl, lineHeight, width])
    
      // 그리기
      const draw = useCallback (
        (ctx: CanvasRenderingContext2D, canvas: HTMLCanvasElement) => {
         // === 중략  ===
         
         // Load Image | 이미지 로드 호출
         loadImage(...)
         
        }, [ bgImageSrc, ... ],
      )
    
      // 캔버스 생성
      const createCanvas = () => { // === 중략 === }
    
      useEffect(() => {
        const { canvas, ctx } = createCanvas()
        
        if (canvas && ctx) {  draw(ctx, canvas) }
      }, [draw])
    

성과 및 배운점