Notice
Recent Posts
Recent Comments
Link
«   2024/10   »
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
Tags
more
Archives
Today
Total
관리 메뉴

오늘도 코딩하나

[moneybook]_day4 본문

React/Daily

[moneybook]_day4

오늘도 코딩하나 2024. 8. 29. 23:40

지금까지 짠 코드에 대한 찜찜한 부분들이 많이 있었고,그에 따라 GPT에게 코드 리뷰를 맡겨보았다.

 

[코드 리뷰 및 개선 사항]

 

1. 상태 업데이트 시 콜백 함수 사용

  const handleUpdate = (id, key, event) => {
    let newItem = event.target.innerText;

    if(key === 'price1' || key === 'price2') {
      if(newItem.trim() === '') {
        newItem = 0;
        event.target.innerText = '0';
      }
    }

    // 수정 전 소스
    const newData = tempData.map((item) =>
      item.id === id ? { ...item, [key]: newItem } : item
    )
    setTempData(newData);
    
    // 수정 후 소스
    setTempData(prevData => prevData.map((item) =>
      item.id === id ? { ...item, [key]: newItem } : item
    ))
  }

 

 

 

 

2. 컴포넌트 내 반복 로직 최소화

// 수정 전 소스
{tempData.map((item,i) => ( 
    <tr key={i}>
      <td><input type="checkbox" checked={checkedItems.includes(item.id)} onChange={() => handleCheck(item.id)} disabled={disabledItems.includes(item.id)} /></td>
      <Input ref={el => inputRefs.current[i * 7 + 0] = el} onBlur={(e) => handleUpdate(item.id, 'date', e)} onKeyDown={(e) => handleKeyDown(e, (i+1)*7+0)} onClick={(e)=>setInitial(e, item)} onKeyUp={(e)=>setInitial(e, item)}>{item.date}</Input>
      <Input ref={el => inputRefs.current[i * 7 + 1] = el} onBlur={(e) => handleUpdate(item.id, 'group1', e)} onKeyDown={(e) => handleKeyDown(e, (i+1)*7+1)} onClick={(e)=>setInitial(e, item)} onKeyUp={(e)=>setInitial(e, item)}>{item.group1}</Input>
      <Input ref={el => inputRefs.current[i * 7 + 2] = el} onBlur={(e) => handleUpdate(item.id, 'group2', e)} onKeyDown={(e) => handleKeyDown(e, (i+1)*7+2)} onClick={(e)=>setInitial(e, item)} onKeyUp={(e)=>setInitial(e, item)}>{item.group2}</Input>
      <Input ref={el => inputRefs.current[i * 7 + 3] = el} onBlur={(e) => handleUpdate(item.id, 'price1', e)} onKeyDown={(e) => handleKeyDown(e, (i+1)*7+3)} onClick={(e)=>setInitial(e, item)} onKeyUp={(e)=>setInitial(e, item)}>{!isNaN(item.price1) ? Number(item.price1).toLocaleString('ko-KR') : item.price1}</Input>
      <Input ref={el => inputRefs.current[i * 7 + 4] = el} onBlur={(e) => handleUpdate(item.id, 'price2', e)} onKeyDown={(e) => handleKeyDown(e, (i+1)*7+4)} onClick={(e)=>setInitial(e, item)} onKeyUp={(e)=>setInitial(e, item)}>{!isNaN(item.price2) ? Number(item.price2).toLocaleString('ko-KR') : item.price2}</Input>
      <Input ref={el => inputRefs.current[i * 7 + 5] = el} onBlur={(e) => handleUpdate(item.id, 'payment', e)} onKeyDown={(e) => handleKeyDown(e, (i+1)*7+5)} onClick={(e)=>setInitial(e, item)} onKeyUp={(e)=>setInitial(e, item)}>{item.payment}</Input>
      <Input ref={el => inputRefs.current[i * 7 + 6] = el} onBlur={(e) => handleUpdate(item.id, 'remark', e)} onKeyDown={(e) => handleKeyDown(e, (i+1)*7+6)} onClick={(e)=>setInitial(e, item)} onKeyUp={(e)=>setInitial(e, item)}>{item.remark}</Input>
    </tr>
))}


// 수정 후 소스
const columns = ['date', 'group1', 'group2', 'price1', 'price2', 'payment', 'remark'];
...
{tempData.map((item,i) => ( 
  <tr key={i}>
    <td><input type="checkbox" checked={checkedItems.includes(item.id)} onChange={() => handleCheck(item.id)} disabled={item.disabled} /></td>
    {columns.map((col, idx) => (
      <Input
        key={col}
        ref={el => inputRefs.current[i * 7 + idx] = el}
        onBlur={(e) => handleUpdate(item.id, col, e)}
        onKeyDown={(e) => handleUpdate(e, (i + 1) * 7 + idx, e)}
        onClick={(e) => setInitial(e, item)}
        onKeyUp={(e) => setInitial(e, item)}>
          {col.includes('price') && !isNaN(item[col]) ? Number(item[col]).toLocaleString('ko-KR') : item[col]}
      </Input>
    ))}
  </tr>
))}

소스코드의 중복 방지를 위해 styled-components를 활용한 Input 컴포넌트를 재사용했다.

하지만 Input 컴포넌트를 사용하는 부분에서 onBlur, onKeyDown 등 반복 코드가 많았고, GPT는 이 점을 지적했다.

그래서 columns 배열을 이용하여 반복을 줄였다.

 

 

 

3. 상태 관리의 단순화

// 수정 전 소스
let [disabledItems, setDisabledItems] = useState([]);
...
setDisabledItems(disabledItems.filter(itemId => itemId !== item.id));


// 수정 후 소스
const [tempData, setTempData] = useState(
	payList.map(item => { ...item, isDisabled: false })
);
...
setTempData([...tempData, { id: tempData.length, isDisabled: true }]);

체크박스 선택 불가한 항목들에 대한 id를 disabledItems에 저장하여 활용하는 방안을 택했었는데,

tempData에 isDisabled 필드를 추가하는 방안으로 변경했다.

 

 

 

4. 유틸리티 함수 분리와 모듈화

export const selectText = (e) => {
    const range = document.createRange();
    const selection = window.getSelection();

    range.selectNodeContents(e.target);
    selection.removeAllRanges();
    selection.addRange(range);
}

App.js 파일에 포함되어 있던 selectText 함수를 별도의 파일(./util/selectText.js)로 만들어서 재사용 가능하게 관리하는 방향으로 변경했다.

다른 컴포넌트나 파일에서 중복 없이 쉽게 사용할 수 있게 되었다.

 

 

 

5. Redux 상태 관리의 비파괴적 업데이트

let payList = createSlice({
    name : 'payList',
    initialState,
    reducers : {
        updatePayList(state, action) {
        	// 수정 전 소스
            return action.payload;
            // 수정 후 소스
            return [ ...state, ...action.payload ];
        }
    }
})

payList를 복제한 tempData를 수정 후 updatePayList 함수를 호출하여 완전히 payList를 덮어쓰게 했다.

하지만 보통 Redux에서는 피과적인 업데이트가 권장됨에 따라 상태를 복사하여 업데이트하는 방식으로 개선했다.

'React > Daily' 카테고리의 다른 글

[moneybook]_day6  (0) 2024.09.02
[moneybook]_day5  (0) 2024.08.30
[moneybook]_day3  (0) 2024.08.24
[moneybook]_day2  (0) 2024.08.24
[moneybook]_day1  (0) 2024.08.22