최근 키보드를 새로 샀다.

요즘은 축이 다양해지고 커스텀 옵션의 폭도 넓어져 실제로 사용해보지 않는 한 키감은 정확히 알 수 없으나, 소리만큼은 유튜브 등에서 후기 포스트를 통해 들어본다.

다만 이런 식으로 키보드를 사는 나에게 가장 어려운 점은- 녹음 환경이 달라, 같은 제품이라도 영상마다 소리가 많이 다르다는 점이다.

 

소리가 너무나 마음에 들어 키보드 없이 노트북만 들고 외출한 경우에도 듣고 싶었다

그래서 이 키보드의 소리를 최대한 객관적으로 들을 수 있는 가상 키보드 프로젝트를 시작해보았다.

 

 

 

 


 

 

 

 

 

이번에는 Typescript 기반의 React에 Vite를 사용하고, 스타일링은 Tailwind를 사용한다.

 

Vite 설치 방법

npm create vite@latest
react-ts //원하는 프레임워크와 템플릿 작성

 

Vite 개발 서버 실행 방법

npm run dev
yarn dev

 

 

 

 

 


 

 

 

 

 

 

 

우선 피그마로 키보드의 레이아웃을 잡고, LED가 켜진 상태, 꺼진 상태 이미지를 제작했다.

브라우저 크기에 따라 이미지 화질이 저하되는 것을 방지하기 위해 벡터 기반으로 그려 SVG로 추출했다.

 

 

 

무지개색 LED 레이어는 추후 마스크로 사용하여 움직이는 배경 애니메이션을 제작할 예정이다.

우상단의 원형 버튼은 LED를 끄고 켜는 기능을 한다.

 

 

위 이미지를 브라우저 중앙에 위치시키고, LED 관련 레이어를 같은 좌표에 위치시켰다.

Keys 컴포넌트에 해당 기능을 먼저 만들고, 상위 컴포넌트에 props로 넘겨주어 on/off 여부에 따라 다른 레이어가 나타나도록 했다.

//Keys.tsx

<button onClick={onLightToggle} className="wheel">
  <span>LED</span>
</button>
//App.tsx

function App() {

  const [lightOn, setLightOn] = useState<boolean>(false);
  const lightHandler = () => {
    setLightOn(!lightOn);
  };

...

return (
...
 <div className="base">
    <Keys
    	onLightToggle={lightHandler}	//넘겨준 onLightToggle prop에 적절한 함수 연결
    />
    <img src={keyboard} alt="keyboard" className="w-full h-auto" />
    	{lightOn ? (
    <img src={keyboardOn} alt="keyboard" className="on" />
    ) : (
    <img src={keyboardOff} alt="keyboard" className="off" />
    )}
 </div>
 
 )
 }

 

 

나머지 버튼을 레이아웃 위치에 맞게 정렬했다.

이해를 돕기 위해 버튼마다 임시로 주황색을 입혔다. 분홍색 사각형은 각각을 묶은 단위.

 

 

 

정렬 방법은- 스타일이 많이 다른 ESC 키와 LED 키는 별개로 다루고, Fuction 키 열과 나머지 Main열을 각각 배열로 만들었다.

모든 키가 같은 규격을 갖지 않고, 각 열마다 조금씩 차이가 있었다.

따라서 다른 길이나 간격이 필요한 경우, extraClass 항목을 추가하여 스타일링했다.

//keyArray.ts

interface Key {
  label: string;
  extraClass?: string;
}

export const functionKeys = [
  ["F1", "F2", "F3", "F4"],
  ["F5", "F6", "F7", "F8"],
  ["F9", "F10", "F11", "F12"],
];

export const mainKeys: Key[][] = [	//2차원 배열
  // 첫번째 줄
  [
    { label: "~" },
    { label: "1" },
    { label: "2" },
    { label: "3" },
    ...
  ],
  // 두번째 줄
  [
    { label: "TAB", extraClass: "w-[9.4%]" },
    { label: "Q" },
    ...
  ],
  
  ...
  
  ];

 

 

 

 

이제 이 키를 map메소드를 통해 화면에 뿌려준다.

return (
    <div className="key-container">
      <div className="quad-key-row">
        <button className="esc">
          <span>ESC</span>
        </button>

        <div className="quad-container">
          {functionKeys.map((row, rowIndex) => ( // functionKeys 배열을 가져와 반복
            <div className="quad-row" key={rowIndex}>
              {row.map((key) => (
                <button
                  className="quad-keys"
                  key={key}
         		>
                  <span>{key}</span>
                </button>
              ))}
            </div>
          ))}
        </div>

        <button onClick={onLightToggle} className="wheel">
          <span>LED</span>
        </button>
      </div>

      <div className="key-main-container">
        {mainKeys.map((row, rowIndex) => (	// mainKeys 배열을 가져와 열마다 반복
          <div className="key-main-row" key={rowIndex}>	// 각 열을 지정했다면
            {row.map((label, labelIndex) => (	// 그 내부 키를 반복하여 지정
              <button
                className={`main-keys ${label.extraClass || ""}`} // 항목에 extraClass가 있다면 추가
                key={labelIndex}
              >
                <span>{label.label}</span>
              </button>
            ))}
          </div>
        ))}
      </div>
    </div>
  );

 

 

 

+ Recent posts