220503 Today I learned!
- JavaScript Ajax 실습
- 스터디 프로젝트
- 백준 문제 풀이
1. Fact
(1) JavaScript Ajax
Ajax를 통해 DJango에서 비동기 처리하는 것을 구현
- 좋아요와 팔로우 AJAX 요청으로 처리한다.
- 로그인 사용자가 좋아요 or 팔로우 버튼을 누름에 따라 버튼이 바뀐다.
- 실시간으로 좋아요 수, 팔로워 수 , 팔로잉 수가 반영된다.
- 새로고침 없이 발생해야 한다.
서버로 요청을 보내고
AJAX
요청하는 url
method
csrf
서버에서는 요청에 맞는 JSON 응답한다
views.py
- 요청에 맞는 데이터
- JsonResponse
받은 응답을 DOM에 반영한다.
JS
.then()
- 태그 생성 및 속성 변경
(2) 스터디 프로젝트
코드 간소화
- 소모 칼로리를 표현하는 칼로리바를 반복문을 사용하여 코드를 간소화했다.
- Array.map을 활용해 컴포넌트를 render 했다. (자세한 내용은 Finding에 정리했다.)
- 코드 재사용성을 높이기 위해 Card컴포넌트에 있는 함수를 상위 컴포넌트로 이동시켰다. 그리고 해당 함수를 props로 넘겨받았다.
1
2
3
4
const deleteData = (num) => {
const newData = data.filter((d) => d.id !== num);
setData(newData);
};
위의 코드를 App.js로 이동했다. 만약 다른 컴포넌트에서 데이터를 삭제할 일이 있다면 해당 함수를 props로 전달하여 코드를 재사용하기 위함이다.
- 데이터 pk(id) 설정하는 방법을 조금 수정했다.
1
2
3
4
5
6
7
8
9
10
11
// 처음 코드
// 새로고침하면 id가 0으로 리셋이 되기 때문에 새로고침 후 생성한 데이터의 pk가 겹친다.
const [index, setIndex] = useState(0);
// 이전 코드
// 데이터를 중간에서 삭제하면 데이터의 길이가 줄어들기 때문에 마지막 id와 새로 생성한 id가 겹친다.
const [index, setIndex] = useState(data.length);
// 현재 코드
// 가장 마지막에 있는 데이터의 id에 +1을 하기 때문에 중간에 데이터를 삭제해도 id가 겹치지 않는다.
const [index, setIndex] = useState(data[data.length - 1].id + 1 || 1)
(3) 백준 문제 풀이
- 2579 계단 오르기
- 문제풀이
- DP 바텀업 방식
- 연속 3개의 계단을 밟으면 안된다는 조건이 까다로웠다.
- 단순 재귀방법에서는 밟는 계단의 수를 카운트하면 되었지만, 바텀업에서는 이런 방식을 적용할 수 없다고 판단했다.
- 그래서 조건에 맞는 기저사례를 만들고 바텀업방식으로 풀었다.
- 그런데 풀고 보니 계단의 수가 300개 였기때문에 간편하게 탑다운 방식으로 풀어도 괜찮지 않았을까라는 생각이 들었다. (아님말구..)
2. Feeling
역시 프로젝트는 재밌다. 혼자서 고민하고 해결하는 재미가 있는 것 같다. 하지만 내 코드가 과연 좋은 코드인지에 대한 의문이 계속 든다. 과연 좋은 코드는 무엇이고 어떻게 구현을 해야 하는 걸까. 참 어렵다. 이건 내가 리액트에 대한 지식이 부족하기 때문인 것같다. 왜 리액트에서는 함수형 컴포넌트를 쓰고, 컴포넌트를 나눠서 개발하고 , Hook은 어떻게 쓰고 리액트은 어떤 점이 좋은지에 대해 잘 모르겠다. 그래서 프로젝트 이후 이에 대해서 학습하는 시간을 갖고 싶다. 😊
3. Finding
반복랜더링은 return이 있어야 한다! ㄴo0oㄱ (너무 당연한건데.. 생각을 못했다… 👀)
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
62
63
/* component/card.js (수정 전 코드) */
function Card({ data, deleteData }) {
return (
/* 생략... */
{detailData.calories <= 50 ? (
<StyledCircleContainer>
<StyledExpBarRed />
<StyledExpBarGrey />
<StyledExpBarGrey />
<StyledExpBarGrey />
<StyledExpBarGrey />
<StyledExpBarGrey />
</StyledCircleContainer>
) : detailData.calories <= 100 ? (
<StyledCircleContainer>
<StyledExpBarRed />
<StyledExpBarRed />
<StyledExpBarGrey />
<StyledExpBarGrey />
<StyledExpBarGrey />
<StyledExpBarGrey />
</StyledCircleContainer>
) : detailData.calories <= 150 ? (
<StyledCircleContainer>
<StyledExpBarRed />
<StyledExpBarRed />
<StyledExpBarRed />
<StyledExpBarGrey />
<StyledExpBarGrey />
<StyledExpBarGrey />
</StyledCircleContainer>
) : detailData.calories <= 200 ? (
<StyledCircleContainer>
<StyledExpBarRed />
<StyledExpBarRed />
<StyledExpBarRed />
<StyledExpBarRed />
<StyledExpBarGrey />
<StyledExpBarGrey />
</StyledCircleContainer>
) : detailData.calories <= 250 ? (
<StyledCircleContainer>
<StyledExpBarRed />
<StyledExpBarRed />
<StyledExpBarRed />
<StyledExpBarRed />
<StyledExpBarRed />
<StyledExpBarGrey />
</StyledCircleContainer>
) : (
<StyledCircleContainer>
<StyledExpBarRed />
<StyledExpBarRed />
<StyledExpBarRed />
<StyledExpBarRed />
<StyledExpBarRed />
<StyledExpBarRed />
</StyledCircleContainer>
)}
<StyledCircleContainer />
/* 생략... */
);
}
위의 코드는 사용자가 입력한 칼로리를 각 조건에 따라 어떻게 표현되는지 보여준다. (6칸으로 되어있는 경험치바로 50, 100, 150… 단위로 빨간색 칸이 증가한다. 예를 들어, 200kal를 소모했다면 빨간색 칸 4개 회색칸 2개로 구성된 경험치 바를 랜더링한다.
처음에는 단순하게 조건부 랜더링을 했다. 하지만 작성하면서도 이 코드는 나중에 수정을 해야겠다 마음을 먹었고 어떻게 반복랜더링을 할 수 있을지 고민해 보았다.
1) 우선 칼로리 데이터에 접근을 해볼까?
1
2
const dataCalories = data.map((data) => data.calories);
console.log(dataCalories);
2) 아 ! 데이터의 id가 있어야 하네!
id에 접근하기 위해서는 결국 template에서 가져와야 했다. 그래서 script구간에서 미리 함수를 만들어놓고 랜더링을 해봤다.
데이터의 칼로리를 인자로 전달하여 함수를 template에서 실행 하면 되지 않을까… 라고 생각을 했었다. 🙄
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
function Card({ data, deleteData }) {
const expBar = (num) => {
let redStart = 0;
let greyStart = 0;
// 경험치바는 최대 6칸이기 때문에 6보다 큰수가 오면 6칸으로 처리한다.
const red = num >= 6 ? 6 : num;
const grey = 6 - red;
while (redStart < red) {
<StyledExpBarRed />
redStart += 1;
}
while (greyStart < grey) {
<StyledExpBarGrey />
greyStart += 1;
}
};
return (
/* 생략...
칼로리를 50으로 나눈 값을 인자로 넘긴다.(Math.floor()를 통해 소숫점은 버린다. ) */
<StyledCircleContainer>
{expBar(Math.floor(detailData.calories / 50))}
</StyledCircleContainer>
)
3) 오류는 발생하지 않지만 컴포넌트가 보이지 않았다.
생각해보니 당연한 이유였다. return이 없기 때문이다. (머쓱타드)
처음에 localStorage에 있던 데이터 배열을 map 메서드로 카드 컴포넌트를 만든 것은 별 생각 없이 만들었다. 지금와서 생각해보니 반복할때마다 return을 해주기때문에 카드 컴포넌트를 생성할 수 있었던 것이다!
이를 위해서는 경험치를 표현하는 배열을 만들어야 했다.
- 예를 들어 ‘200kal만큼 운동을 했다’라고 가정하면, 빨간색칸은 4칸 회색 칸은 2칸이여야 한다.
red = [0, 0, 0, 0] , grey = [0, 0]
과 같이 만든다.- template부분에서 map을 통해 해당 배열의 길이만큼 컴포넌트를 생성하는 것이다.
이것이 깔끔한 방법인지는 모르겠지만, 내가 아는 반복 랜더링 방법은 map 뿐이였기 때문에 배열을 만들 수 밖에 없었다.
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
function Card({ data, deleteData }) {
const greyExp = (num) => {
const red = num >= 6 ? 6 : num;
const grey = 6 - red;
const greyArray = new Array(grey).fill(0); // [0, 0, 0, 0]
return greyArray;
};
const redExp = (num) => {
const red = num >= 6 ? 6 : num;
const redArray = new Array(red).fill(0); // [0, 0]
return redArray;
};
return (
/* 생략...
칼로리를 50으로 나눈 값을 인자로 넘긴다.(Math.floor()를 통해 소숫점은 버린다. )
key는 해당 데이터의 `id-칼로리바색-index`로 구성했다. */
<StyledCircleContainer>
{redExp(Math.floor(detailData.calories / 50)).map(
(item, index) => {return (<StyledExpBarRedkey={`${detailData.id}-red-${index}`}/>);
}
)}
{greyExp(Math.floor(detailData.calories / 50)).map(
(item, index) => {return (<StyledExpBarGreykey={`${detailData.id}-grey-${index}`}/>);
}
)}
</StyledCircleContainer>
)
4. Future Action & Feedback
Future Action | 진행 상황 | Feedback |
---|---|---|
DOM을 깨우치다 | pause 🤦♀️ | 잠시 중단! |
1일 1알고! 🔥 | in progress 🚀 | |
why 리액트? | Not Start🌙 |