react-simple-keyboard 라이브러리로 한글 입력 가상 키보드 구현하기 (function component 예제)

김혜림·2024년 2월 24일
0

react

목록 보기
1/12
post-thumbnail

키오스크 프로그램을 개발하면서 한국어 타이핑이 가능한 가상 키보드를 구현해야 했다.

react-simple-keyboard라는 좋은 라이브러리가 있지만, 한글 받침입력이 안된다는 치명적인 오류가 있었다.

누군가가 react-simple-keyboard 라이브러리를 개선해서 한글 키보드 입력이 가능하게끔 수정한 라이브러리를 npm에 올려놨다. 이걸 사용해도 되긴 하겠지만 그래도 기왕 구현하는거 내가 직접 한글 입력이 가능하도록 수정해 보았다.

참고 : react-simple-keyboard의 데모가 전부 class형으로 구현되어 있어서 좀 해맸다. 🫠

👉🏻 CodeSandbox에서 보기

설치하기

  • react simple keyboard : npm i react-simple-keyboard
  • hangul-js : npm install hangul-js

구현하기

  1. react simple keyboard를 커스텀할 것이므로, CustomKeyboard.jsx 를 생성해주고, 그 안에서 react-simple-keyboard 라이브러리의 <Keyboard /> 컴포넌트를 가져왔다.
    (CSS는 styled component로 작성했다.)

CustomKeyboard.jsx

import React from "react";
import Keyboard from "react-simple-keyboard";
import styled from "styled-components";

const CustomKeyboard = ({ text, setText }) => {
  return (
    <KeyboardWrapper>
      <Keyboard />
    </KeyboardWrapper>
  );
};

const KeyboardWrapper = styled.div``;

export default CustomKeyboard;

이렇게 작성해주면...

오.. 이게뭐야~ CSS가 없어서이다. react-simple-keyboard에서는 기본적으로 제공해주는 CSS코드가 있다. import 해보자.
  • react-simple-keyboard의 기본 CSS import 하기 : import "react-simple-keyboard/build/css/index.css";

CSS가 잘 import 되었다.
이제 영문 자판을 한글 자판으로 바꿔보자.
react-simple-keyboard의 layout을 아래의 배열로 바꿔주면 된다.

koreanLayout.js

export const koreanLayout = {
  default: [
    "ㅂ ㅈ ㄷ ㄱ ㅅ ㅛ ㅕ ㅑ ㅐ ㅔ",
    "ㅁ ㄴ ㅇ ㄹ ㅎ ㅗ ㅓ ㅏ ㅣ",
    "{shift} ㅋ ㅌ ㅊ ㅍ ㅠ ㅜ ㅡ {pre}",
    "{space} {dot} {enterText}",
  ],
  shift: [
    "ㅃ ㅉ ㄸ ㄲ ㅆ ㅛ ㅕ ㅑ ㅒ ㅖ",
    "ㅁ ㄴ ㅇ ㄹ ㅎ ㅗ ㅓ ㅏ ㅣ",
    "{shift} ㅋ ㅌ ㅊ ㅍ ㅠ ㅜ ㅡ {pre}",
    "{space} {dot} {enterText}",
  ],
};

CustomKeyboard.jsx

import React from "react";
import Keyboard from "react-simple-keyboard";
import styled from "styled-components";
import "react-simple-keyboard/build/css/index.css";
import { koreanLayout } from "./koreanLayout";

const CustomKeyboard = ({ text, setText }) => {
  return (
    <KeyboardWrapper>
      <Keyboard layout={{ ...koreanLayout }} />
    </KeyboardWrapper>
  );
};

const KeyboardWrapper = styled.div``;

export default CustomKeyboard;

layout 설정해주었더니 한글 자판이 잘 보인다.
이제 input 태그와 키보드를 연결해보자.

<CustomKeyboard />에 input 태그의 state, setState를 인자로 주고,
CustomKeyboard.jsx에서 <Keyboard />onKeyPress 속성에 함수를 아래와 같이 설정한다.

App.jsx

...
function App() {
  const [text, setText] = useState("");

  return (
    <PageContainer>
      <CustomInput value={text} onChange={(e) => setText(e.target.value)} />
      <CustomKeyboard text={text} setText={setText} />
    </PageContainer>
  );
}

CustomKeyboard.jsx

...
const CustomKeyboard = ({ text, setText }) => {
  const onKeyPress = (key) => {
    setText((prev) => prev + key);
  };

  return (
    <KeyboardWrapper>
      <Keyboard 
      layout={{ ...koreanLayout }} 
      onKeyPress={onKeyPress} />
    </KeyboardWrapper>
  );
};

onKeyPress에서 prev+key 만 해줬더니 {shift}, {dot}, {enterText}, {pre} 는 제대로 동작하지 않는다.

한글 외의 키를 눌렀을 때 처리도 해주겠다.

CustomKeyboard.jsx

const CustomKeyboard = ({ text, setText }) => {
  const [layoutName, setLayoutName] = useState("default"); // default, shift

  const onKeyPress = (key) => {
    if (key === "{pre}") {
      const res = text.slice(0, -1);
      setText(res);
    } else if (key === "{shift}") {
      setLayoutName((prev) => (prev === "default" ? "shift" : "default"));
    } else if (key === "{enterNum}" || key === "{enterText}") {
      console.log("enter clicked!");
    } else if (key === "{dot}") {
      setText((prev) => prev + ".");
    } else if (key === "{space}") {
      setText((prev) => prev + " ");
    } else {
      setText((prev) => prev + key);
    }
  };

  return (
    <KeyboardWrapper>
      <Keyboard
        layoutName={layoutName}
        layout={{ ...koreanLayout }}
        onKeyPress={onKeyPress}
      />
    </KeyboardWrapper>
  );
};

특수 키를 눌렀을 때의 처리가 잘 되었다.
<Keyboard /> layoutName 속성에 'default'를 주면 koreanLayoutdefault가 출력되고, shift를 주면 koreanLayoutshift가 출력된다.

자 이제 어떤 문제가 있냐하면

위와 같이 한글 키 조합이 안되는 문제가 생긴다.

이를 해결하기 위해 hangul-js 라이브러리를 사용한다.

CustomKeyboard.jsx

const CustomKeyboard = ({ text, setText }) => {
  const [layoutName, setLayoutName] = useState("default"); // default, shift

  const onKeyPress = (key) => {
    if (key === "{pre}") {
      const res = text.slice(0, -1);
      setText(res);
    } else if (key === "{shift}") {
      setLayoutName((prev) => (prev === "default" ? "shift" : "default"));
    } else if (key === "{enterNum}" || key === "{enterText}") {
      console.log("enter clicked!");
    } else if (key === "{dot}") {
      setText((prev) => prev + ".");
    } else if (key === "{space}") {
      setText((prev) => prev + " ");
    } else {
      setText((prev) => hangul.assemble(hangul.disassemble(prev + key)));
    }
  };

  return (
    <KeyboardWrapper>
      <Keyboard
        layoutName={layoutName}
        layout={{ ...koreanLayout }}
        onKeyPress={onKeyPress}
      />
    </KeyboardWrapper>
  );
};

와우~ 한글 입력이 잘된다.

hangul-js의 assemble : ['ㅇ','ㅏ','ㄴ'] 와 같은 배열을 입력받아 '안'으로 바꿔준다.
hangul-js의 disassemble : '안'와 같은 문자열을 입력받아 ['ㅇ','ㅏ','ㄴ'] 와 같은 배열로 바꿔준다.

기존 hangul-js에서는 hangul.assemble() 에 prev + key 를 그대로 전달해줬기 때문에 받침이 입력되지 않았던 것이다.

(근데 window에서 개발할 때는 받침이 안되는 오류를 확인했었는데 mac에서는 disassemble 안써도 또 한글 받침이 잘 먹는다 ㅋㅋ 골때리네.)

이제 고치고 싶은게 또 있다. 바로 {dot}, {shift} ... 이런 디자인을 고치는 것.

CustomKeyboard.jsx 에서 <Keyboard /> 컴포넌트에 display속성을 다음과 같이 추가해준다.

CustomKeyboard.jsx

...
return (
    <KeyboardWrapper>
      <Keyboard
        layoutName={layoutName}
        layout={{ ...koreanLayout }}
        onKeyPress={onKeyPress}
        display={{
          "{enterText}": "Enter",
          "{shift}": "↑",
          "{.}": ".",
          "{space}": " ",
          "{dot}": ".",
          "{pre}": "←",
        }}
      />
    </KeyboardWrapper>
  );

짠. 버튼 글자도 내가 원하는대로 커스텀했다.

profile
개발 일기입니다. :-)

0개의 댓글