🤔왜 비동기 데이터 처리여야 했나?
Reddit 클론 프로젝트를 구현하는 데에 있어서 중요했던 것 중 하나는 동기식 및 비동기식 데이터 처리에 익숙해지는 것이었다.
동기적 데이터 처리는 직관적이고 간단하며 프로그램이 선형 흐름을 따르기 때문에 실행 경로를 추적하고 발생할 수 있는 모든 오류를 정확하게 파악하는 것이 쉽다. 또한, 데이터 처리량이 상대적으로 적고 속도가 크게 중요하지 않은 소규모 응용 프로그램에 적합하다.
그러나 동기식 데이터 처리에는 여러 가지 단점도 있는데, 가장 큰 단점 중 하나는 대량의 데이터를 처리할 때 느리고 비효율적일 수 있다는 것이다. 프로그램이 각 작업이 완료될 때까지 기다리기 때문에, 일련의 작업을 완료하는 데 시간이 오래 걸릴 수 있다. 이로 인해 사용자 경험에 부정적인 영향을 미칠 수 있기 때문에, 나는 더 나은 사용자 경험을 위해 이해하기 불편하고 어려워 보였던 비동기적 데이터 처리라는 불구덩이에 뛰어들기로 했다(...🔥😇)
그리하여 내가 시도한 것은 Redux Toolkit
의 createAsyncThunk
함수를 사용하여 비동기 데이터를 처리하는 방식이었다.createAsyncThunk
를 사용한 예시 중 하나는 사용자가 본 게시물에 대한 데이터를 가져오는 것이다.
예시 코드 1
해당 함수는 액션 유형을 나타내는 문자열과 데이터를 반환하는 비동기 함수 두 가지 인수를 취한다.
const getCurrentPost = createAsyncThunk(
"currentPost/getCurrentPost",
async (postid) => {
const resp = await axios.get("/api/currpost", {
params: { postid: postid },
});
return resp.data;
}
);
const currentPost = createSlice({
name: "currentPost",
initialState: {
list: [],
},
extraReducers: (builder) => {
builder.addCase(getCurrentPost.pending, (state, action) => {
});
builder.addCase(getCurrentPost.fulfilled, (state, action) => {
state.list = action.payload;
});
builder.addCase(getCurrentPost.rejected, (state, action) => {
});
},
});
getCurrentPost 함수는 createAsyncThunk
를 사용하여 생성되며, postid를 매개변수로 사용하는 dispatch
메서드를 가지고 있다. 함수는 axios
라이브러리를 사용하여 postid 매개변수에 해당하는 서버 측 라우트에 대한 비동기 GET 요청을 수행합니다. 응답이 수신되면 함수는 데이터를 반환한다.
그 다음, getCurrentPost 함수를 사용하여 currentPost라는 상태 슬라이스를 만든다. extraReducers
속성을 사용하여 대기 중(pending
), 완료(fulfilled
) 및 거부(rejected
)와 같은 비동기 호출의 다양한 상태를 처리했다. 대기 중(pending
) 상태가 트리거되면 상태에 아무 작업도 수행하지 않고, 완료(fulfilled
) 상태가 트리거되면 액션 페이로드(action.payload
)인 비동기 호출의 응답 데이터를 상태의 list 속성으로 설정했다.
예시 코드 2
개별 게시물 데이터를 가져오는 것 외에도, 사이드메뉴에서 토픽 및 서브토픽 데이터를 검색하는 기능도 구현해야 했는데, 게시물과 마찬가지로 extraReducers 속성을 사용하여 비동기 호출의 다른 상태를 처리했다. 아래 코드가 해당 구현 코드다.
const asyncTopic = createAsyncThunk("communitySlice/asyncTopic", async () => {
const resp = await axios.get("/api/topic");
return resp.data;
});
const topicSlice = createSlice({
name: "topicSlice",
initialState: {
list: [],
},
extraReducers: (builder) => {
builder.addCase(asyncTopic.pending, (state, action) => {
// console.log("pending");
});
builder.addCase(asyncTopic.fulfilled, (state, action) => {
state.list = action.payload;
// console.log("fulfilled");
});
builder.addCase(asyncTopic.rejected, (state, action) => {
// console.log("rejected");
});
},
});
const asyncSub = createAsyncThunk("subSlice/asyncSub", async () => {
const resp = await axios.get("/api/sub");
return resp.data;
});
const subSlice = createSlice({
name: "subSlice",
initialState: {
list: [],
},
extraReducers: (builder) => {
builder.addCase(asyncSub.pending, (state, action) => {
// console.log("pending");
});
builder.addCase(asyncSub.fulfilled, (state, action) => {
state.list = action.payload;
// console.log("fulfilled");
});
builder.addCase(asyncSub.rejected, (state, action) => {
// console.log("rejected");
});
},
});
Redux Toolkit
의 createAsyncThunk
함수를 사용하면 비동기 데이터 처리 과정을 단순화할 수 있다. 여러 상태에 대한 복수의 리듀서를 작성하지 않고도 비동기 작업을 생성할 수 있기 때문이다.
해당 기능을 구현하느라 씨름을 해야했지만, 비동기 데이터를 처리하는 좋은 방법에 대해 배우고 익힐 수 있었다는 점에서 만족스러운 경험이었다. 또한 비동기와 동기, 두가지 데이터 처리 방식에 대해 알아보고, 또 장단점과 차이점, 어느 곳에 적합한지 연구하며 개발자로서 더 나은 사용자 경험에 대해 생각해볼 수 있는 시간이었다.