React로 설문조사 페이지 만들기

Haram B·2022년 7월 7일
2

Frontend

목록 보기
2/5
post-thumbnail

최종 만들어진 화면✨

💡Index

1. Router

2. Form

3. 다양한 input 태그들

4. Rest-Api에 DB 연동 후 설문 조사 결과 저장하기 (feat. axios)

📌 1. Router

가장 처음은 프로젝트를 생성하는 것이지, 암.

npx create-react-app "name"

다음은 router를 설정해주기 위해 react-router-dom을 설치한다.
react-router-dom doc📄

npm i react-router-dom

설치가 모두 끝나면 App.js에서 router를 지정해 줄 수 있다.

<BrowserRouter>
	<MainNavLinks />
    <Routes>
    	<Route path="/" element={<HomePage />} />
        <Route path="/survey" element={<SurveyPage />} />
        ...(중략)...
        </Routes>
</BrowserRouter>

가장 위에 BrowserRouter가 있고, Routes 안에 Route들을 넣어주고 element로 연동할 page들을 넣어준다.
path는 프로젝트를 실행하면 가장 처음 루트 링크가 '~/' 인데 이 뒤에 '~/home' 또는 '~/survey' 등으로 붙여주면 해당 페이지로 라우터가 된다고 지정해 주는 것이다.

각 Router들에 링크를 연결해주어야 하는데, 나는 그냥 Link 태그가 아닌 NavLink를 사용했고, component로 따로 만들어 주었기 때문에 <MainNavLinks /> 태그가 중간에 있다.

export function MainNavLinks() {
  const activeStyle = {
    color: "#00ADB5",
    fontWeight: "bold",
  };
  const defaltStyle = {
    color: "#393E46",
  };
  return (
    <MenuDiv>
      <ul style={{ display: "flex" }}>
        <StyledLi>
          <NavLink
            to="/"
            style={({ isActive }) => (isActive ? activeStyle : defaltStyle)}
          >
            Home
          </NavLink>
        </StyledLi>
        ...(중략)...
      <Divider />
    </MenuDiv>
  );
}

MainNavLinks의 코드는 위와 같다. NavLink는 그냥 Link 태그와 다르게 activeStyle을 지정해주어서 링크가 active 되었을 때, 즉 클릭 되었을 때 스타일을 정의해 줄 수 있다.

📌 2. Form

html 과 동일하게 form 태그 안에 input 태그들을 넣어서 input 태그의 value들을 submit 할 수 있다.

// inputs이라는 object를 state로 선언한다.
const [inputs, setInputs] = useState({
	name: "",
    age: "",
);
const onSubmit = (e) => {
	const { value, name } = e.target;
    setInputs({
    // inputs이 object 형태이므로 이런 식으로 해줘야 기존의 inputs에서 데이터를 추가할 수 있다.
      ...inputs,
      [name]: value,
    });
}
// form 기본 형태
<form action="/" onSubmit={onSubmit}>
	<input ~~~ />
</form>

위의 기본 형태를 이해한다면 정말 웬만한 input 태그 (type="number", "text", "date", "checkbox" 등등)들로 form을 작성할 수 있을 것이다.

📌 3. 다양한 input 태그들

하지만 내가 좀 더 신경쓰고 어려웠던 것은 여러개의 checkbox를 만드는 것, 그리고 마지막 box 옆에는 text type의 input 태그를 넣어서 checkbox의 value가 아니라 옆의 text 의 value를 submit 되게 하는 것이었다.

결론부터 이야기 하자면,
checkbox의 선택 여부를 확인하는 false로 이루어진 array state 하나, text의 value를 저장하는 string 타입의 state 하나, text type input의 disabled 여부를 결정해줄 boolean type state 하나, checkbox에 value를 넣어주는 변수 array 하나 총 4개의 데이터 타입을 선언해준다.
그러니까 이렇게.

// n에는 만들고 싶은 checkbox 개수를 넣으면 된다.
const [checkedIdx, setCheckedIdx] = useState(Array(n).fill(false);
const [etcValue, setEtcValue] = useState("");
// true, false가 좀 헷갈릴 수 있는데 변수 이름이 disabled라 true로 초기화 해줬다.
const [isDisabled, setDisabled] = useState(true);
let checkedValue = [];

만들어주고 map을 사용해서 여러개의 checkbox를 생성해 준다.

const CheckBoxes = () => {
...
	return items.map((item, index) => (
		<div key={index}>
        	<input
          	type="checkbox"
          	name={name}
          	onChange={(e) => checkTheBox(e.target.checked, index)}
          	checked={checkedIdx[index]}
        />
        	<label>{item}</label>
		</div>
	));
}

onChange에 보면 checkTheBox라는 함수가 있고 e.target.checked와 index를 파라미터로 보내주는데, 이 함수를 통해 앞에서 생성한 false 배열에서 체크 된 곳의 인덱스에 해당하는 부분을 true로 만들어준다.

const checkTheBox = (checked, index) => {
	checkedIdx[index] = checked; // true가 또는 false가 알아서 들어가겠쥬?
    setCheckedIdx([...props.checked]);
    // 꼭 state를 변경해주어서 선택한 값들이 날라가지 않도록 하자
}

이렇게 하면 위에 checkbox map 부분에서 checked라는 property도 지정해 줄 수 있어서 해당 index의 값이 true이면 체크 된 상태, false이면 체크가 되지 않은 상태로 잘 남아있는다.

문제의 input type="text" 태그 value는..
위에서 만든 etcValue와 isDisabled state를 이용할 것이다.
checkbox map에서 가장 끝에 box 옆에 text input를 만들면

~~~ 기타

<CheckBoxes /><label>기타</label><input type="text" disabled={isDisabled}/>
이런식으로 있겠쥬?
일단 위의 checkTheBox 안에 아래의 코드를 추가해 주어서 끝에 있는 box를 선택하면 disabled가 풀릴 수 있도록 해준다

if (index === checkedIdx.length - 1) {
	setDisabled(!isDisabled);
}

그리고 input 태그에 <input type="text" disabled={isDisabled} onChange={(e) => onChange(e.target.value)}/> onChange 함수를 추가해 주어서 text를 쓰면 value가 etcValue state에 들어갈 수 있도록 함수를 작성해준다.

const onChange = (value) => {
	setEtcValue(value);
}

여기서 주의 할 점은 onChange 함수가 text input과 같은 함수 안에 있으면 한글자씩만 입력됩니다~!!! (렉 먹은 것처럼 엑 하고 입력😵 엑 하고 입력😵 됨)

const InputText = () => {
	const onChange = (value) => {
		setEtcValue(value);
	}
    return <input type="text" />
}

🙆🏻‍♂️

const onChange = (value) => {
	setEtcValue(value);
}
...
return <input ~~ />

이렇게!!
이거 때문에 한동안 고생했다..

그 다음에 이제 값을 넣어 줄 때에는
다시 한번 map을 사용해서 checkedIdx를 돌아 true값인 부분의 index에 해당하는 item을 넣어주고 (여기서 item은 checkbox의 label을 지정해준 배열입니다!)
text input 옆의 체크 박스(ex 기타)가 선택이 되어있으면 (isDisabled가 false이면) text input 옆의 체크 박스의 label(ex 기타) 값을 찾아서 그 인덱스에 원래 label이 아닌 etcValue를 넣어준다.

checkedIdx.map((checked, idx) => {
	// true이면 그 인덱스에 맞는 item 을 넣어준다
    if (checked) {
      checkedValue.push(items[idx]);
    }
    return null; // retur을 뭐라도 안해주면 warning이 나서 넣었습니다.
});
if (!isDisabled) {
    var idx1 = checkedValue.findIndex((el) => el === '기타');
    checkedValue[idx1] = etcValue;
}

어우 이부분에서 말이 길어지네요.. 이렇게 하면 이제 checkedValue에 아름답게 내가 넣고자 하는 checkbox들의 label 값들과 입력한 값들이 잘 들어가 있는다
inputs이라는 object를 form이 끝난 후 rest api에 submit 할 예정이므로 위에 나왔던 inputValue 함수 말고!! 따로 작성할 submit 함수에 이렇게 넣어준다.
inputs.(~~~) = checkedValue;

시간 부족(?)으로 빠르게 submit 함수를 정리해보고 넘어가야 할 것 같다.
submit은 form 태그 가장 아래 (그래도 form 태그 안에 있어야 함!) button을 submit type으로 넣어주면 form 태그 안에 있는 onSubmit 함수가 실행 된다

const onSubmit = (e) => {
	e.preventDefault(); // 자동 새로고침 방지
    ...
    passDataToApi(inputs);
}
return <form onSubmit={onSubmit}>
	...
	<button type="submit">submit</button>
</form>

요로코롬~

passDataToApi()는 방금 급하게 지어봤는데 엄청 구린 함수 이름 같군,,
어쨌든 rest-api로 넘어가 보겠습니다

📌 Rest-Api에 DB 연동 후 설문 조사 결과 저장하기 (feat.axios)

항상 처음은 설치부터..!
doc: https://axios-http.com/kr/docs/intro
설치: npm install axios

axios는 문서에 보면 "Promise 기반 HTTP 클라이언트"라고 나오는데
내가 이해하기로는 rest-api(backend)와 post 또는 get 형식(url을 보내는 방식이다. 으로 소통할 수 있는 library라고 이해했다.
post는 보통 데이터를 저장하거나 삭제할 때 많이 쓰이고 get에는 데이터를 불러오거나 상세한 데이터(/~/{~} 이런식으로)를 불러올 때 많이 쓰인는 것 같다.

form이 submit 되면서 넘어온 value들을 저장해야하므로 post를 사용한다.

async function passDataToApi(inputs) {
  await axios({
    method: "post",
    url: "http://~~~/survey",
    data: {
      birthYear: inputs.birthYear,
      ...
    },
  })
    .then(function (response) {
      console.log(response);
    })
    .catch(function (error) {
      console.log(error);
    });
}

아주 간단하다.

데이터 연동하고 불러오기 참고
데이터 저장하기

이 두 링크가 많은 도움이 되었다!

// 데이터 가져오기 맛뵈기
const [data, setData] = useState(null);
useEffect(() => {
	const fetchData = async () => {
    	try{
        	const res = axios.get('https://~/~/{~}'); // 여기 데이터를 불러오고자 하는 url을 넣으면 된다.
            setData(res.data);
            } catch (e) {
            	//state를 사용해서 error를 저장해줘도 됨
                console.log(e)
            }
	};
    fetchData();
}, []);

설문조사 입력하는 페이지 말고도 로그인, 회원가입, 데이터 불러와서 표로 보여주는 페이지를 만들었다.
일단 메인 페이지(?)인 설문조사 페이지에서 기억나는 내용들 먼저 끄적여 봤다..✍🏻

그럼 이만 안뇨옹😘

profile
사회에 이로운 IT 기술에 대해 고민하고 모두가 소외되지 않는 서비스를 운영하는 개발자를 꿈꾸고 있습니다 ✨

0개의 댓글