이 글을 지난 9월 11일부터 15일까지 진행된 미니 프로젝트의 과정을 아카이빙하기 위해 작성되었습니다.
Wereads
라는 가상의 서비스를 개발하는 것이 1차 프로젝트의 목표였습니다.React는 컴포넌트 단위로 개발하는 JavaScript 라이브러리인 만큼, 페이지 컴포넌트에 여러 컴포넌트를 import하여 구성하는 방식을 선택했습니다.
위 스크린샷에서도 확인할 수 있듯이 Button을 기능별로 관심사 분리했습니다. 간단하게 BackButton을 살펴보겠습니다.
// BackButton.js
<button
className="back-btn"
type={type}
aria-label="뒤로 가기"
onClick={goToBack}
>
<BackIcon />
뒤로
</button>
className
, type
, onClick
에aria-label
을 더해서 웹 접근성 처리를 하도록 했습니다. 따라서 스크린 리더기(NVDA, VoiceOver 등)에서는 BackButton에 focus한 순간, 뒤로 가기 버튼이라 읽어줄 것입니다.history.go(-1)
과 같은 기능을 수행하는 navigate(-1)
을 함수로 만들어 onClick
이벤트 핸들러에 연결했습니다. 이로써 BackButton은 어떤 페이지 컴포넌트에서 import 하더라도 자신의 기능을 문제 없이 수행할 수 있게 되었습니다.// Button.js
function Button(props) {
const {
type = 'button',
shape = 'solid',
scale,
text,
action,
disabled,
onClick,
} = props;
// props
// - type: [String] button(default) / submit / reset
// - shape: [String] solid(default) / outline / text
// - scale: [String] large(default) / small
// - text: [String]
// - action: [String] delete
// - disabled: 조건이 상이하므로 페이지마다 다른 삼항 조건문 적용
return (
<button
className="btn"
type={type}
shape={shape}
scale={scale}
aria-label={text}
action={action}
onClick={onClick}
disabled={disabled}
>
{text}
</button>
);
}
type
의 기본값은 button으로 정했습니다. submit인 로그인과 회원가입 페이지에서의 버튼만 value를 바꿔주면 됩니다.shape
props는 outline이 없는 경우와 있는 경우, 그리고 단순한 텍스트형으로 구분한 결과입니다.scale
은 크기별, text
는 버튼 명칭과 웹 접근성 처리에 사용되었고, action
의 경우는 수정/삭제라는 기능별로 분리하기 위해 선언한 props입니다.disabled
는 button 태그의 attribute로, 로그인 및 회원가입 페이지에서 유효성 검증에 통과하지 못하면 true 값을 받습니다.:postId
로 동적 라우팅
을 구현한 소스 코드입니다.@charset "utf-8";
// other colors
$blue: #2d71f7;
$navy: #083e7f;
$red: #ff3636;
// grayscale
$grayscale-1: #000;
$grayscale-2: #999;
$grayscale-3: #ccc;
$grayscale-4: #e0e0e0;
$grayscale-5: #fafafa;
$grayscale-6: #fff;
$grayscale-7: #e6e6e6;
$pallette: (
(lightTheme, #000, #999, #ccc, #e0e0e0, #fafafa, #fff),
(darkTheme, #fff, #fafafa, #e0e0e0, #ccc, #999, #000)
);
@mixin ThemeTransition {
transition:
background-color 0.25s ease-in-out,
color 0.25s ease-in-out;
}
@charset "utf-8";
@import '../../styles/partials/mixin', '../../styles/partials/theme';
@mixin theme(
$theme,
$grayscale-1,
$grayscale-2,
$grayscale-3,
$grayscale-4,
$grayscale-5,
$grayscale-6
) {
[theme='#{$theme}'] {
@include ThemeTransition;
background-color: $grayscale-6;
.post-view {
display: flex;
flex-direction: column;
width: 100%;
.post-list {
.post-item {
padding: 0 8px 24px;
border-bottom: 1px $grayscale-3 solid;
}
}
}
}
}
@each $theme, $grayscale-1, $grayscale-2, $grayscale-3, $grayscale-4,
$grayscale-5, $grayscale-6 in $pallette
{
@include theme(
$theme,
$grayscale-1,
$grayscale-2,
$grayscale-3,
$grayscale-4,
$grayscale-5,
$grayscale-6
);
@mixin Position(
$position,
$top: null,
$right: null,
$bottom: null,
$left: null
) {
position: $position;
top: $top;
right: $right;
bottom: $bottom;
left: $left;
}
@mixin FlexCenter {
display: flex;
justify-content: center;
align-items: center;
}
@mixin Ir() {
overflow: hidden;
color: transparent;
font-size: 1px;
}
@mixin Ellipsis($multi: null, $line: null) {
@if $multi {
display: -webkit-box;
overflow: hidden;
text-overflow: ellipsis;
-webkit-line-clamp: $line; /* 라인수 */
-webkit-box-orient: vertical;
word-wrap: break-word;
white-space: normal;
} @else {
display: block;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
word-wrap: break-word;
}
}
component
별로 담당자를 지정해 브랜치 이름과 정렬했습니다. feature
, 수정사항은 fix
로 협의했습니다.useEffect(() => {
setLoading(true);
fetch('/data/postData.json', {
method: 'GET',
header: {
'Content-Type': 'application/json',
Authorization: localStorage.getItem('accessToken'),
},
})
.then(res => res.json())
.then(data => {
// const result = data.getThread;
// setDataList(result);
// mock data
setDataList(data);
setLoading(false);
});
}, []);
- Node.js
- Express
- MySQL
- Bcrypt
- JWT
- Git & Github
infinite scroll
을 구현하고자 했습니다. 모든 데이터를 일괄적으로 받아오는 것이 아닌 하단부에 스크롤이 닿으면 다음 데이터를 로딩하는 방식으로 UI/UX 측면이나 데이터 관리 측면에서 효율적이라 생각했으나, 기술적인 한계에 부딪혀서 다음을 기약하게 되었습니다.+1
, -1
로 보내줘야 합니다만, 일정상 구현할 수 없었습니다.이 프로젝트의 의도는 무엇인가?
였습니다.