본문 바로가기

Next.js

내 사이트 성능 개선하기(2)

 

이번에는 내 포트폴리오 사이트의 접근성을 개선해보고자 한다.

접근성 지수가 높아야 봇이 웹 사이트의 정보를 잘 가져갈 수 있어서 SEO에 유리할 수 있다.

 

접근성에 유리한 요소는 접근 가능한 라벨, 색 대비, 시멘틱 태그 등이 있다.

 

 

1. 버튼에 접근 가능한 이름 붙이기

사이드바를 여는 버튼에 네이밍이 없다는 결과를 받았다.

이 부분을 개선해보자

 

<button
  className={cn(
    'outline-none',
    'border-none',
    'cursor-pointer',
    'absolute',
    'top-4',
    'left-4',
    'w-12',
    'h-12',
    'rounded-full',
    'bg-transparent',
    'flex',
    'justify-center',
    'items-center'
  )}
  onClick={setToggle}>
  <svg width="23" height="23" viewBox="0 0 23 23">
    <Path
      variants={{
        closed: { d: 'M 2 2.5 L 20 2.5' },
        open: { d: 'M 3 16.5 L 17 2.5' }
      }}
    />
    <Path
      d="M 2 9.423 L 20 9.423"
      variants={{
       closed: { opacity: 1 },
        open: { opacity: 0 }
      }}
      transition={{ duration: 0.1 }}
    />
    <Path
      variants={{
        closed: { d: 'M 2 16.346 L 20 16.346' },
        open: { d: 'M 3 2.5 L 17 16.346' }
      }}
    />
  </svg>
</button>

버튼에 접근성에 대한 정보가 없으니 추가해주자.

 

<button
  aria-label="사이드바 열기"
  role="button"
  className={cn(
    'outline-none',
    'border-none',
    'cursor-pointer',
    'absolute',
    'top-4',
    'left-4',
    'w-12',
    'h-12',
    'rounded-full',
    'bg-transparent',
    'flex',
    'justify-center',
    'items-center'
  )}
  onClick={setToggle}>
  <svg width="23" height="23" viewBox="0 0 23 23">
    <Path
      variants={{
        closed: { d: 'M 2 2.5 L 20 2.5' },
        open: { d: 'M 3 16.5 L 17 2.5' }
      }}
    />
    <Path
      d="M 2 9.423 L 20 9.423"
      variants={{
       closed: { opacity: 1 },
        open: { opacity: 0 }
      }}
      transition={{ duration: 0.1 }}
    />
    <Path
      variants={{
        closed: { d: 'M 2 16.346 L 20 16.346' },
        open: { d: 'M 3 2.5 L 17 16.346' }
      }}
    />
  </svg>
</button>

 

 

2. 시멘틱 태그 개선하기

마크업 과정 중에 li 요소를 빠트린 것 같다. 확인해보자.

<ul className="flex justify center gap-8 max-sm:flex-col">
  {blogData.map(({ link, imgSrc, title }, index) => (
    <MotionSlide className="z-10" key={link} delay={(index + 1) * 0.5}>
      <li
        className={cn(
          '2xl:w-96',
          '2xl:h-60',
          'z-10',
          'xl:w-80',
          'xl:h-[12.5rem]',
          'lg:w-72',
          'lg:h-[11.25rem]',
          'w-52',
          'h-[8.125rem]'
        )}>
        <Link
          className="block w-full h-full rounded-lg overflow-hidden"
          href={link}
          target="_blank">
            <Image
              width={0}
              height={0}
            sizes="100vw"
            style={{
              width: '100%',
              aspectRatio: '8 / 5',
              overflow: 'hidden'
            }}
            loading="lazy"
            src={imgSrc}
            alt={title}
          />
        </Link>
      </li>
    </MotionSlide>
  ))}
</ul>

위 부분이 문제가 되는 코드인데 Framer motion을 Wrapping 하는 MotionSlide 컴포넌트가 내부적으로 motion.div로 div 태그를 렌더링하고 있어서 생긴 이슈로 보인다.

 

MotionSlide 컴포넌트를 보자

 

'use client';
import { motion, useInView, useAnimation } from 'framer-motion';
import { useRef, useEffect, ElementType } from 'react';

type Props = {
  children: React.ReactNode;
  className?: string;
  delay?: number;
  translateDirection?: 'x' | 'y';
};

const MotionSlide = ({
  children,
  delay,
  className,
  translateDirection = 'x'
}: Props) => {
  const ref = useRef(null);
  const isInview = useInView(ref, { once: true });
  const controls = useAnimation();
  const directionHidden =
    translateDirection === 'x' ? { translateX: 90 } : { translateY: 90 };
  const directionVisible =
    translateDirection === 'x' ? { translateX: 0 } : { translateY: 0 };
  useEffect(() => {
    if (isInview) {
      controls.start('visible');
    }
  }, [isInview, controls]);

  return (
    <motion.div
      ref={ref}
      variants={{
        hidden: { opacity: 0, ...directionHidden },
        visible: { opacity: 1, ...directionVisible }
      }}
      transition={{
        type: 'spring',
        duration: 0.2,
        damping: 8,
        delay: delay,
        stiffness: 100
      }}
      initial="hidden"
      animate={controls}
      className={className}>
      {children}
    </motion.div>
  );
};

export default MotionSlide;

 

내부적으로 div 태그를 렌더링 하고 있다.

태그를 props로 전달받아 시멘틱하게 렌더링할 수 있도록 바꿔보자.

'use client';
import { motion, useInView, useAnimation } from 'framer-motion';
import { useRef, useEffect, ElementType } from 'react';

type Props = {
  children: React.ReactNode;
  className?: string;
  delay?: number;
  translateDirection?: 'x' | 'y';
  as?: ElementType;
};

const MotionSlide = ({
  children,
  delay,
  className,
  translateDirection = 'x',
  as: Component = 'div'
}: Props) => {
  const ref = useRef(null);
  const isInview = useInView(ref, { once: true });
  const controls = useAnimation();
  const directionHidden =
    translateDirection === 'x' ? { translateX: 90 } : { translateY: 90 };
  const directionVisible =
    translateDirection === 'x' ? { translateX: 0 } : { translateY: 0 };
  useEffect(() => {
    if (isInview) {
      controls.start('visible');
    }
  }, [isInview, controls]);

  const MotionComponent = motion[
    Component as keyof typeof motion
  ] as ElementType;

  return (
    <MotionComponent
      ref={ref}
      variants={{
        hidden: { opacity: 0, ...directionHidden },
        visible: { opacity: 1, ...directionVisible }
      }}
      transition={{
        type: 'spring',
        duration: 0.2,
        damping: 8,
        delay: delay,
        stiffness: 100
      }}
      initial="hidden"
      animate={controls}
      className={className}>
      {children}
    </MotionComponent>
  );
};

export default MotionSlide;
<ul className="flex justify center gap-8 max-sm:flex-col">
  {blogData.map(({ link, imgSrc, title }, index) => (
    <MotionSlide
      as="li"
      className={cn(
        '2xl:w-96',
        '2xl:h-60',
        'z-10',
        'xl:w-80',
        'xl:h-[12.5rem]',
        'lg:w-72',
        'lg:h-[11.25rem]',
        'w-52',
        'h-[8.125rem]'
      )}
      key={link}
      delay={(index + 1) * 0.5}>
      <Link
        className="block w-full h-full rounded-lg overflow-hidden"
        href={link}
        target="_blank">
        <Image
          width={0}
          height={0}
          sizes="100vw"
          style={{
            width: '100%',
            aspectRatio: '8 / 5',
            overflow: 'hidden'
          }}
          loading="lazy"
          src={imgSrc}
          alt={title}
        />
      </Link>
    </MotionSlide>
  ))}
</ul>

이렇게 변경하면 태그를 원하는 대로 변경할 수 있다!

 

이렇게 전부 변경하고 결과 값을 보면...!!

79점 -> 96점으로 상승한 것을 볼 수 있다.

 

더 공부해서 100점까지 목표로 해보고자 한다.