💡 블록스코프와 전역 스코프는 반의어다. 전자는 대개 중괄호로 감싼 코드 블록을 말한다. if-else문, switch-case문을 그 예로 들 수 있다. 그러나 중괄호를 생략한다고 전역 스코프가 되는 것은 아니다.
블록 스코프와 엇비슷한 개념이 함수 스코프다. 그 이름에서 보이듯 function 키워드로 감싼 중괄호를 말하는데, 이 또한 별도의 스코프가 생겨서 함수 내부의 변수는 함수 밖에서 사용할 수 없다. 그러려면 return 등 외부로 꺼내는 작업이 필요하다.
모달과 토글은 함께 쓰일 수밖에 없는 짝꿍이다. 모달 하나만 쓸 땐 단순하지만, 여러 모달을 동시에 보여주고 없애려다보면 문제가 발생할 수 있다.
if (response.data.result) {
setToggleState();
// TODO : 등록되었습니다 모달 왜 안 나옴
onModal();
}
...
{modal && (
<>
{console.log('hi')}
<ModalBasic
type="confirm"
content="등록"
toggleState={true}
setToggleState={onModal}
/>
{console.log('after')}
</>
)}
우선 토글 기능을 하는 modal
이 동작하는지 알아보려고 첫번째 콘솔을, 모달 컴포넌트가 렌더링되는지 보려고 두번째 콘솔을 찍었다.
그런데 hi만 찍히고 after는 찍히지 않았다! 개발자도구도 살펴보니 모달 컴포넌트가 아예 없다. onModal 하나만 실행해보니 이건 또 제대로 동작한다.
즉 첫번째 모달이 실행되자마자 두번째 모달을 실행되는 문제라면, setTimeout
을 사용해보자.
if (response.data.result) {
onModal();
setTimeout(() => {
setToggleState((prevState) => !prevState);
}, 2000);
}
.productCard {
margin: 0 25px 40px;
max-width: 400px;
width: 20vw; // <-- 크기 고정
background-color: #f5f5f5;
border-radius: 5px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
cursor: pointer;
display: flex;
flex-direction: column;
h4 {
padding: 10px;
color: #333;
width: 100%; // <-- 상위 요소의 100% 차지
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
text-overflow: ellipsis;
@include responsiveText(18px, 14px);
}
Member.key
로 하고 싶을 때.
로 객체의 key를 타고 들어가는 거니까 객체 내 객체로 만들어주면 된다.
const formattedReviews = reviews.map((review) => ({
review: review.review,
title: review.title,
isAnonymous: review.isAnonymous,
stars: review.stars,
boardId: review.Board.boardId,
memberId: review.Member.memberId,
createdAt: review.createdAt,
updatedAt: review.updatedAt,
Member: {
nickname: review.Member.nickname,
},
}));
//App.js
...
function App() {
return (
<BrowserRouter>
<Navbar />
<Routes>
<Route exact path="/" element={<Main />} />
...
</Routes>
<Footer />
</BrowserRouter>
);
}
export default App;
상황 : Navbar처럼 각 컴포넌트에 적용할 필요 없이 App.js에서 설정하려고 했는데 요소가 계속 겹쳐서 같은 방식을 쓸 수 없었다.
해결 : fixed하지 않고, 그냥 보통의 컴포넌트처럼 페이지마다 컴포넌트에 넣어뒀다.
.footer {
margin: var(--header-margin) 0;
width: 100%;
padding: 20px;
text-align: center;
font-size: 12px;
opacity: 0.6;
.footerContent {
max-width: 800px;
margin: 0 auto;
}
}
body {
overflow-y: scroll;
}
useEffect(() => {
let timeoutId;
if (type === 'confirm') {
timeoutId = setTimeout(() => {
disableModal();
}, 3000);
}
return () => {
clearTimeout(timeoutId);
};
}, [toggleState]);
타임아웃 함수는 언마운트 할 때 비워주지 않으면, 이전에 사용한 함수가 그대로 남아있다. 고로 설정한 시간(3초)가 되기 전에 이미 실행되었다고 간주했던 것이다.
// 서버 컨트롤러
exports.findAccount = async (req, res) => {
try {
const targetMemberId = req.params.memberId;
const member = await Member.findOne({
where: { memberId: targetMemberId },
});
// 결제정보 없음
if (!member.accountNum || !member.bankName) {
return res.send({
result: false,
userData: { accountNum: '등록 정보 없음',
bankName: '등록 정보 없음' },
});
// 결제정보 있음
} else {
return res.send({
result: true,
userData: { accountNum: member.accountNum,
bankName: member.bankName },
});
}
} catch (err) {
console.error(err);
res.status(500).send('서버 에러');
}
};
// MyPage.js
// 이미 등록된 결제 정보 확인
const hasAccountCk = async () => {
const response = await axios({
method: 'get',
url: `${process.env.REACT_APP_DB_HOST}${endpoint}`,
});
if (response.data) {
onAccountToggle(); // 모달 1(결제정보 내역) show
const data = response.data.userData;
setAccountInfo({ // 데이터 세팅
accountNum: data.accountNum,
bankName: data.bankName,
});
if (response.data.result) {
return true; // 토글로 사용
} else {
return false;
}
}
};
...
<div
className={`myAccount ${
accountToggle ? 'slideIn' : 'slideOut'
}`}
onClick={hasAccountCk}
>
결제정보 등록/확인 >
</div>
{accountToggle && (
<ModalAccount
accountInfo={accountInfo}
setToggleState={onAccountToggle}
setAccountInfo={setAccountInfo}
/>
)}
이어서 또 다른 문제.
if (member.accountNum === 'NULL' || member.bankName === 'NULL')
리액트의 관건은 컴포넌트 재사용이라고 생각해서 이미 사용 중인 컴포넌트를 최대한 재사용하고 싶었다. 그래서 다른 팀원이 만든 Review
컴포넌트의 일부를 ReviewList
로 컴포넌트화 했다.
두 페이지에 같은 컴포넌트를 사용할 수 있게 하려다보니 공통적으로 쓰이는 함수와 변수들이 보였고, 이를 커스텀 훅으로 만들었다. 그러고서 기존 코드에 맞춰 데이터바인딩 하려 했으나.. 수정/삭제 기능 앞에서 말짱 도루묵이 되었다.
💡 이유를 모르겠다고 쓰려던 찰나 갑자기 가설 하나가 떠올랐다! 필요한 컴포넌트를 덜 가져와서 데이터는 있는데 바인딩되지 않았던 게 아닐까?!
이게 맞다면, 댓글 수정/삭제 기능이 안 된 미스테리도 풀린다. 내일 해봐야지!!
/static/bankLogo/금융아이콘_SVG_KB.svg
/static/bankLogo/ㄱㅡㅁㅇㅠㅇㅇㅏㅇㅣㅋㅗㄴ_SVG_KB.svg
해결 :
encodeURIComponent
에 전체 경로를 넣어주어 해결
<img
src={`/${encodeURIComponent(
bankData.logoBasePath + bank.logoPath
)}`} />
<div className="chattingBoardOne">
<img
src={`${
process.env.REACT_APP_DB_HOST
}static/userImg/${encodeURIComponent(boardInfo.image)}`}
alt="board img"
/>
</div>
이렇게 프로젝트 기간이 끝나고, 지난 목요일에 발표 후 피드백을 들었다.
그리고 다른 팀들 발표를 보면서 마지막 3차 프로젝트엔 무엇을 더하면 좋을지 생각해봤다.
일정 관리, 프로젝트 관리 :
구글 시트 등 문서 파일을 활용한 팀들이 확실히 꼼꼼한 확인이 가능해 보였다.
보안 서버(https) 사용
sns 로그인 :
레퍼런스로 참고한 사이트 대다수가 간편 로그인을 지원 중이었다.
업데이트 계획 :
업데이트 할 수 있다는 건 그만큼 토대가 잘 짜여졌기 때문에 덧댈 수 있는 거라고 본다.
scss + .module -> styled component로 사용하는 방법도 있다.
백드롭 필터 :
CSS 설정 중 하나인데, hover랑 같이 쓰면 예쁠 것 같다.
sass를 제대로 구조화해서 사용할 수 있어 좋았다.
라이브러리나 프레임워크를 거의 안 쓴 것도 아쉽다. 하물며 API로 가져오는 작업도 안 했으니.. 다양한 라이브러리를 사용해 보고 싶다.
🐞 :
더 많이 배우고 싶다.. 💪