
기본적인 레이아웃 작업이 끝나서 상태관리 작업을 할 단계가 왔습니다.
처음 생각난건 useState였지만, 너무 많이 사용해봤기 때문에 다른 기술을 사용하고 싶었습니다.
구글링해보니 리액트의 상태관리로 redux가 널리 알려져있다고 하는데
처음엔 useReducer인줄 알았지만 둘이 다른거라고 합니다.
결론: redux로 결정!!!
1 | npm install redux react-redux | cs |
상태관리하기 전의 코드는 다음과 같습니다.
QuestionInput는 현재 사용자가 입력할 내용을 뜻합니다. 먼저, QuestionInput를Redux로 상태관리 해주겠습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | import { ChatContainer, ChatElement, Chating, Chatbot, Profile, Answer, User, Question, ChatSend, QuestionInput, SendImg, } from "./ChatPage.style.js"; const ChatPage = () => { return ( <> <ChatContainer> <ChatElement> <Chating> <Chatbot> <Profile src='/image/Impolite_chatbot.png' alt='챗봇 프로필' /> <Answer>무슨 고민 있음?</Answer> </Chatbot> <User> <Question>나 회사 상사 때문에 너무 스트레스 받아</Question> </User> </Chating> <ChatSend> <QuestionInput placeholder='Chatbot에게 하고싶은 말을 입력해주세요!' /> <SendImg src='/image/Send.png' alt='화살표' /> </ChatSend> </ChatElement> </ChatContainer> </> ); }; export default ChatPage; | cs |
폴더 구조는 다음과 같습니다.
1 2 3 4 5 6 7 8 9 10 | src/ |-- redux/ | |-- store.js | |-- actions.js | |-- reducer.js |-- pages/ | |-- ChatPage.js | |-- ChatPage.style.js |-- index.js | cs |
Redux에서 애플리케이션의 전체 상태를 관리하는 store를 생성하는 코드입니다.
1 2 3 4 5 6 7 | import { createStore } from "redux"; import rootReducer from "./reducer"; const store = createStore(rootReducer); export default store; | cs |
createStore : Redux에서 제공하는 함수로, 스토어를 생성해주는 역할을 합니다.
rootReducer : 이전에 작성한 리듀서를 가져옵니다.
const store = createStore(rootReducer) : rootReducer를 인수로 전달하는 스토어를 생성해줍니다.
현재 reducer.js에서는 하나의 모듈만 내보내지고 있는 상태입니다. JavaScript에서는 파일을 import할 때 그 파일 내부에 하나의 모듈만 존재한다면 이름을 자유롭게 지정할 수 있습니다.
rootReducer은 Redux에서 관습적으로 사용하는 이름입니다. 굳이 이 이름이 아니라도 상관은 없지만 다른 분들 코드를 살펴보니 rootReducer을 주로 사용하는 것 같습니다.
Redux에서 상태를 업데이트하기 위해 사용되는 액션 생성자(액션을 만드는 함수)입니다.
1 2 3 4 | export const setQuestionInput = (input) => ({ type: "SET_QUESTION_INPUT", payload: input, }); | cs |
setQuestionInput : 이름
input : 받는 인수
"SET_QUESTION_INPUT" : 액션이 어떤 일을 하는지 설명
input : 추가 정보
1 2 3 4 5 6 | //결과 { type: "SET_QUESTION_INPUT", payload: "유저가 입력한 텍스트" } | cs |
Redux에서 애플리케이션의 상태를 관리하는 핵심 부분인 reducer를 정의하는 코드입니다.
이 코드에서는 'SET_QUESTION_INPUT'이라는 액션이 들어오면 'questionInput' 값을 업데이트 해줍니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | const initialState = { questionInput: "", }; const reducer = (state = initialState, action) => { switch (action.type) { case "SET_QUESTION_INPUT": return { ...state, questionInput: action.payload, }; default: return state; } }; export default reducer; | cs |
initialState : 처음 만들어질 때 가질 기본 상태를 정의
reducer : 현재 상태와 액션을 받아 새로운 상태를 반환해줍니다.
state = initialState : 리듀서가 처음 호출될 때 state가 정의되지 않은 경우 initialState를 기본값으로 사용합니다.
swith 문
1 2 3 4 5 6 7 8 9 | switch (action.type) { case "SET_QUESTION_INPUT": return { ...state, questionInput: action.payload, }; default: return state; } | cs |
switch문을 사용하여 액션의 type에 따라 어떤 상태 업데이트를 할지 결정합니다.
여기서 action.type는 액션 객체에서 보내준 type입니다.
ex) type: "SET_QUESTION_INPUT"
case가 "SET_QUESTION_INPUT"일 때 실행될 수 있도록 구현해줍니다.
'...state'는 현재 상태를 복사하는 코드입니다. 상태의 다른 속성들이 손상되지 않도록 기존 상태를 유지하면서 일부 속성만 업데이트하기 위해 사용합니다.
questionInput: action.payload은 questionInput 속성을 action.payload로 업데이트하는 코드입니다.
default는 만약 현재의 switch문에 해당하는 case가 없을 경우 현재 상태를 그대로 반환하도록 해줍니다.
QuestionInput에 입력을하고 enter(또는 sendImg)를 클릭할 때 콘솔창에 나오도록 구현했습니다. 화질이 좀 안좋네요..😥

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | import { ChatContainer, ChatElement, Chating, Chatbot, Profile, Answer, User, Question, ChatSend, QuestionInput, SendImg, } from "./ChatPage.style.js"; import { useSelector, useDispatch } from "react-redux"; import { setQuestionInput } from "../../redux/actions"; const ChatPage = () => { const questionInput = useSelector((state) => state.questionInput); const dispatch = useDispatch(); const handleSend = () => { console.log(questionInput); dispatch(setQuestionInput("")); }; const handleKeyDown = (e) => { if (e.key === "Enter") { handleSend(); } }; return ( <> <ChatContainer> <ChatElement> <Chating> <Chatbot> <Profile src='/image/Impolite_chatbot.png' alt='챗봇 프로필' /> <Answer>무슨 고민 있음?</Answer> </Chatbot> <User> <Question>나 회사 상사 때문에 너무 스트레스 받아</Question> </User> </Chating> <ChatSend> <QuestionInput value={questionInput} onChange={(e) => dispatch(setQuestionInput(e.target.value))} onKeyDown={handleKeyDown} placeholder='Chatbot에게 하고싶은 말을 입력해주세요!' /> <SendImg onClick={handleSend} src='/image/Send.png' alt='화살표' /> </ChatSend> </ChatElement> </ChatContainer> </> ); }; export default ChatPage; | cs |