안녕하세요, 폼과 ref 활용 방법을 배웠습니다. 회사에서 썼던 프론트 프레임워크는 도형을 가져다 놓으면 알아서 그림을 그려주고, 이벤트 함수를 링크해서 로직만 짜면 됐었는데, 예제로 경험한 리액트 라이브러리는 수동으로 모든 걸 설정하는 느낌이 들어요. 좀 더 자세히 개념을 익혀보기로 해요.

js의 폼 태그는 브라우저에서 입력된 데이터를 서버가 인식해서 데이터를 처리하게 하는 기능을 하는데, 리액트에선 서버에서 요청이 발생되지 않도록 해줘야한다. 폼 태그에서 onSubmit 이벤트 발생 시(전송 버튼을 클릭하면) 브라우저에서 서버로 요청이 발생해서 상태변수가 초기화되는 문제가 발생하기 때문이다. 핸들러로 이벤트 처리하여 서버로 요청이 전달되지 않도록 처리해야한다.
const handleSubmit = e => {
console.log("form 태그의 기본 동작이 처리되지 않도록 설정")
e.preventDefault();
console.log("서버로 요청이 전달되지 않도록 처리");
console.log("자바스크립트 코드를 이용해서 서버로 값을 전달하고, 전달받은 값을 사용하는 기능을 구현");
};
import { useState } from "react";
function App() {
const [count, setCount] = useState(0);
const handleSubmit = e => {
console.log("form 태그의 기본 동작이 처리되지 않도록 설정")
e.preventDefault();
console.log("서버로 요청이 전달되지 않도록 처리");
console.log("자바스크립 코드를 이용해서 서버로 값을 전달하고, 전달받은 값을 사용하는 기능을 구현");
};
return (
<>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>카운트 증가</button>
<form onSubmit={handleSubmit}>
<button type="submit">전송</button>
</form>
</>
);
}
export default App;
DOM 요소나 React 컴포넌트에 대한 참조를 생성하고 관리하는 데 사용되는 특별한 속성.
render 메서드에서 생성된 DOM 노드나 React 엘리먼트에 접근하는 방법을 제공한다.
포커스, 텍스트 선택 영역, 혹은 미디어의 재생을 관리할 때,
동영상을 직접 실행할 때, 3rd party DOM 라이브러리를 React와 같이 사용할 때
class MyComponent extends Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
}
render() {
return <input ref={this.myRef}/>;
}
handler = () => {
const node = this.myref.current;
node.focus();
}
}
ref 어트리뷰트에 React.createRef()를 통해 생성된 ref를 전달하는 대신, 함수를 전달한다.
전달된 함수는 다른 곳에 저장되고 접근될 수 있는 React 컴포넌트의 인스턴스나 DOM 엘리먼트를 인자로 받는다.

패스워드 입력창의 내용이 0000이면 배경색을 파란색으로, 아니면 붉은색으로 설정
[App.js]
import PasswordChecker from "./PasswordChecker";
export default function App() {
return <PasswordChecker />;
}

clickButton = () => {
if (this.state.password === "0000") {
this.setState({ isValid: true });
} else {
this.setState({ isValid: false });
**this.myInput.focus();**
}
};
render() {
return (
<>
<input type="password"
value={this.state.password}
onChange={this.changePassword}
style={this.state.isValid ? {backgroundColor: "blue"} : {backgroundColor: "red"}}
**ref={x => this.myInput = x}**
/>
<button onClick={this.clickButton}>패스워드 검증</button>
</>
);
}
**import React, { Component } from "react";**
class PasswordChecker extends Component {
state = {
password: "",
isValid: false
};
** myInput = React.createRef();**
changePassword = e => this.setState({ password: e.target.value });
clickButton = () => {
if (this.state.password === "0000") {
this.setState({ isValid: true });
} else {
this.setState({ isValid: false });
**this.myInput.current.focus();**
}
};
render() {
return (
<>
<input type="password"
value={this.state.password}
onChange={this.changePassword}
style={this.state.isValid ? {backgroundColor: "blue"} : {backgroundColor: "red"}}
**ref={this.myInput}**
/>
<button onClick={this.clickButton}>패스워드 검증</button>
</>
);
}
}
export default PasswordChecker;

import ScrollBox from "./ref/ScrollBox";
function App() {
return <ScrollBox />;
}
export default App;
import { Component } from "react";
class ScrollBox extends Component {
scrollBottom = () => {
/*
const scrollHeight = this.myDiv.scrollHeight;
const clientHeight = this.myDiv.clientHeight;
*/
const { scrollHeight, clientHeight } = this.myDiv;
this.myDiv.scrollTop = scrollHeight - clientHeight;
};
scrollTop = () => {
this.myDiv.scrollTop = 0;
};
render() {
const styles = {
outer: {
border: "1px solid black",
height: 300,
width: 300,
overflow: "auto"
},
inner: {
width: "100%",
height: 650,
background: "linear-gradient(white, black)"
}
};
return (
<>
<div style={styles.outer} ref={x => this.myDiv = x}>
<div style={styles.inner}></div>
</div>
<div>
<button onClick={this.scrollTop}>맨 위로 이동</button>
<button onClick={this.scrollBottom}>맨 아래로 이동</button>
</div>
</>
);
}
}
export default ScrollBox;
import React, { Component } from "react";
class ScrollBox extends Component {
myDiv = React.createRef();
scrollBottom = () => {
const { scrollHeight, clientHeight } = this.myDiv.current;
this.myDiv.current.scrollTop = scrollHeight - clientHeight;
};
scrollTop = () => {
this.myDiv.current.scrollTop = 0;
};
render() {
const styles = {
outer: {
border: "1px solid black",
height: 300,
width: 300,
overflow: "auto"
},
inner: {
width: "100%",
height: 650,
background: "linear-gradient(white, black)"
}
};
return (
<>
<div style={styles.outer} ref={this.myDiv}>
<div style={styles.inner}></div>
</div>
<div>
<button onClick={this.scrollTop}>맨 위로 이동</button>
<button onClick={this.scrollBottom}>맨 아래로 이동</button>
</div>
</>
);
}
}
export default ScrollBox;

import { Component } from "react";
class App extends Component {
render() {
return (
<div>
ID: <input type="text" /><br />
PW: <input type="password" /><br />
PW: <input type="password" /><br />
<button type="submit">등록</button>
</div>
);
}
}
export default App;
import { Component } from "react";
class App extends Component {
state = {
userId: "",
userPw1: "",
userPw2: "",
};
/*
changeUserId = e => this.setState({ userId: e.target.value });
changeUserPw1 = e => this.setState({ userPw1: e.target.value });
changeUserPw2 = e => this.setState({ userPw2: e.target.value });
*/
changeUser = e => this.setState({ [e.target.name]: e.target.value });
render() {
const { userId, userPw1, userPw2 } = this.state;
return (
<div>
ID: <input ref={x => this.refUserId = x} type="text" value={userId} name="userId" onChange={this.changeUser} /><br />
PW: <input ref={x => this.refUserPw1 = x} type="password" value={userPw1} name="userPw1" onChange={this.changeUser} /><br />
PW: <input ref={x => this.refUserPw2 = x} type="password" value={userPw2} name="userPw2" onChange={this.changeUser} /><br />
<button type="submit">등록</button>
</div>
);
}
}
export default App;
등록 버튼을 클릭했을 때 동작을 추가
import { Component } from "react";
class App extends Component {
state = {
userId: "",
userPw1: "",
userPw2: "",
};
changeUser = e => this.setState({ [e.target.name]: e.target.value });
clickButton = e => {
// Submit 버튼의 기본 동작을 중지
e.preventDefault();
// 객체 비구조화를 통해 상태변수를 지역변수로 변경
const { userId, userPw1, userPw2 } = this.state;
// 입력 여부 체크
if (userId.trim() === "") {
alert("ID를 입력하세요.");
this.refUserId.focus();
return;
}
if (userPw1.trim() === "") {
alert("PW1를 입력하세요.");
this.refUserPw1.focus();
return;
}
if (userPw2.trim() === "") {
alert("PW2를 입력하세요.");
this.refUserPw2.focus();
return;
}
// 패스워드 일치 여부를 확인
if (userPw1.trim() !== userPw2.trim()) {
alert("PW와 PW 확인이 일치하지 않습니다.");
this.setState({ userPw1: "", userPw2: "" });
this.refUserPw1.focus();
return;
}
// 입력 내용을 alert 창으로 출력
alert(`ID: ${userId}\nPW1: ${userPw1}\nPW2: ${userPw2}`);
};
render() {
const { userId, userPw1, userPw2 } = this.state;
return (
<div>
ID: <input ref={x => this.refUserId = x} type="text" value={userId} name="userId" onChange={this.changeUser} /><br />
PW: <input ref={x => this.refUserPw1 = x} type="password" value={userPw1} name="userPw1" onChange={this.changeUser} /><br />
PW: <input ref={x => this.refUserPw2 = x} type="password" value={userPw2} name="userPw2" onChange={this.changeUser} /><br />
<button type="submit" onClick={this.clickButton}>등록</button>
</div>
);
}
}
export default App;
하루에 실습 예제 7개에 발전시키는 과정까지 진행하는데, 생각보다 양이 많아서 공부만이 답인 것 같습니다. 다시 대학생이 된 기분입니다! 화이팅