프로젝트 및 스터디 모집 웹사이트를 클론하는 프로젝트를 진행했습니다.

 

https://github.com/seongEun95/holu

 

GitHub - seongEun95/holu

Contribute to seongEun95/holu development by creating an account on GitHub.

github.com

 

 

1.  새롭게 경험한 것

1) OAuth (카카오 로그인)

카카오로그인 과정

- 먼저 카카오 로그인을 사용하기 위한 api키를 얻기 위해 https://developers.kakao.com/ 카카오 개발자센터에서 REST API 키를 받았습니다. 

- 카카오개발자센터에서 카카오로그인 등록과정은 다음 노션링크에 정리했습니다. 

https://jumbled-carrot-837.notion.site/f1239dd3fbd34df08588cfc7b8f91037?pvs=4

- 새 글쓰기를 진행하기전에 로그인한 유저만 글을 쓸 수 있도록 하기 위하여  새 글쓰기 버튼을 클릭하면 카카오 로그인 화면으로 이동합니다.

- 로그인이 완료되면 사용자가 로그인 여부를 확인하기 위해 헤더영역의 ui가 변경되도록 했습니다. 로그인 완료 시 유저 정보를 리덕스에 저장하고 그 정보를 헤더 컴포넌트에서 가져와서 이름을 표시했습니다.

 

 

2) 반응형 디자인

 

- 데스크탑 뿐만 아니라 테블릿, 모바일에서도 서비스를 이용하기 위해 미디어쿼리를 이용하여 반응형 디자인을 구현했습니다.

const breakpoints = {
	mobile: 576,
	tablet: 768,
	notebook: 991,
	desktop: 1350,
}; // 미디어 쿼리 분기점

const mq = {
	mobile: `@media (max-width: ${breakpoints.mobile}px)`,
	tablet: `@media (max-width: ${breakpoints.tablet}px)`,
	notebook: `@media (max-width: ${breakpoints.notebook}px)`,
	desktop: `@media (max-width: ${breakpoints.desktop}px)`,
};

export default mq;

- 각각의 분기점을 객체 변수로 담아서 다른 파일에서 공통으로 사용할 수 있도록 했습니다.

const cardWrapCss = css`
	display: grid;
	grid-template-columns: repeat(4, 300px);

	${mq.desktop} {
		grid-template-columns: repeat(3, 300px);
		justify-content: center;
	}

	${mq.notebook} {
		grid-template-columns: repeat(2, 300px);
	}

	${mq.tablet} {
		grid-template-columns: repeat(1, 1fr);
	}
`;

- 미리 설정한 mq 변수값을 가져와서 깔끔하게 코드를 작성할 수 있습니다.

- EmotionCss 공식홈페이지에서 참고를 했습니다. https://emotion.sh/docs/media-queries

 

 

3) 글쓰기 (Create)

글쓰기 페이지

 

- 위 이미지와 같이 여러 데이터를 저장하는 경우 글 등록 시 개별 값을 저장하기 보단 데이터를 한 번에 처리하는 것이 더 관리가 쉬워집니다.

- input 등의 태그들은 value와 name 속성이 존재하여 해당 값을 가져올 수 있지만 디자인을 위해 select태그가 아닌 div태그로 드랍다운을 구현한 경우 입력받는 태그가 아니다보니 value, name속성이 존재하지 않는다. 이 문제를 해결하기 위해 name값과 value값을 추가하는 함수를 작성합니다.

// 유틸리티 함수
export const provideAttr = (name: string, value: any, e: any) => {
	e.target.name = name;
	e.target.value = value;
	return e;
};

- 이벤트 객체의 타겟에 name값과 value속성을 추가하고 name값에는 각 입력정보의 제목, value값은 사용자의 입력값을 받게 됩니다.

// SelectBox 컴포넌트
const handleClickSelectItme = (selectedValue: string, e: any) => {
		onClick?.(provideAttr(name, selectedValue, e));
	};

- 위 provideAttr함수를 사용할 컴포넌트에서 함수 인자로 name값, 유저의 선택된 값, 이벤트 객체를 전달합니다.

 

// SelectBox 컴포넌트를 사용하는 페이지
<SelectBox
	name="progressPeriod"
	value={userInput.progressPeriod}
	label="진행 기간"
	options={OPTIONS_PROGRESS_PERIOD}
	placeholder="기간 미정~6개월 이상"
	onClick={handleClickGetItem}
/>

- 최 상위 컴포넌트(페이지)에서 name값과 value값을 props로 전달합니다.

 

const [userInput, setUserInput] = useState<UserInput>({
		category: '',
		personCount: '',
		onlineOrOffline: '',
		progressPeriod: '',
		skillStack: [],
		deadline: dayjs(),
		position: [],
		contactMethod: '',
		contactDetail: '',
		projectTitle: '',
		contents: '',
	});

	const handleClickGetItem = (e: any) => {
		const { name, value } = e.target;
		setUserInput(prev => ({ ...prev, [name]: value }));
	};

- 결론적으로 유저가 드랍다운에서 값을 클릭하면 handleClickGetItem 함수를 실행하여 이벤트 객체의 타켓 속성의 name과 value값을 구조분해할당으로 변수에 할당합니다.

- setUserInput 함수를 실행하여 각각의 네임 값에 따라 value값을 넣어줍니다.

- name과 value값을 통일하게 되면 값을 받아오는 함수를 공통으로 사용할 수 있어 추후 유지보수하기에도 용이해집니다.

- 마지막으로 벡엔드와 통신을 통해 body에 데이터를 담아 전달하면 됩니다.

 

* 추가

- 라이브러리(MuiDatePicker) 컴포넌트로 감싸서 사용하기

export default function DatePicker({ name, value, label, onChange }: DatePickProps) {
	const handleChangeValue = (value: Dayjs | null) => {
		onChange?.({ target: { name, value } } as any);
	};

	return (
		<React.Fragment>
			<div css={labelCss}>{label}</div>
			<MuiDatePicker name={name} value={value} css={datePickerCss} format="YYYY-MM-DD" onChange={handleChangeValue} />
		</React.Fragment>
	);
}

- 라이브러리를 한 번 더 컴포넌트로 사용하여 다른 컴포넌트의 ui와 획일화된 모습으로 사용할 수 있게 합니다.

- 다른 컴포넌트와 동일하게 props를 전달받기 위해 한 번 더 컴포넌트로 감싸기도 합니다.

MuiDatePicker 컴포넌트

 

 

4) 벡엔드 api작업 전 목업데이터

- 벡엔드 api작업이 완료되기 전에 ui시안을 확인하여 프론트엔드 쪽 선 작업하는 과정을 진행했습니다.

- api가 완료된 후 목업데이터와 실제 api의 데이터 양식이 달라 에러가 발생하였고 이 에러들을 수정하는 작업을 진행했습니다.

- 실제 업무 중에서도 충분히 발생할 수 있는 문제이기에 좋은 경험이라고 생각합니다.

목업데이터 타입 vs 실제데이터

 

 

5) 필터 기능

필터 기능 gif

- 벡엔드 통신을 통해 받아온 데이터를 프론트엔드에서 중첩 필터기능이 적용되는 기능입니다.

cardData.filter(item => {
		let isCategoryPassed = true;
		let isSkillPassed = true;
		let isPositionPassed = true;
		let isProgressMethodPassed = true;
		let isSearch = true;

		if (selectedTab !== 'ALL') isCategoryPassed = item.type === selectedTab;

		...
        
		return isCategoryPassed && isSkillPassed && isPositionPassed && isProgressMethodPassed && isSearch;
	});

- 각각 카테고리별로 변수 초기값을 true로 설정하여 모든 값을 반환하지만 사용자가 필터를 설정하면 조건에 따라 false가 되는 데이터는 필터링이 됩니다.

 

 

6) 북마크

북마크

- 유저가 북마크 버튼을 클릭했을 때 내 관심글 페이지에서 이를 확인할 수 있습니다.

- 북마크 버튼 클릭 시 리덕스에 카드 데이터를 저장하고 저장된 데이터를 내 관심글 페이지에서 map메소드로 렌더하는 과정을 거쳤습니다.

// 내 관심글 페이지
const bookmarkItems = useSelector((state: RootState) => state.bookmark.items);

return (
	{bookmarkItems.length === 0 ? (
		<div css={emptyBookmarkCss}>
			<CgTrashEmpty size={40} />
			<div>내 관심글 없음</div>
		</div>
	) : (
		bookmarkItems.map((item: Bookmark) => <Card key={item.id} {...item} />)
	)}
)

 

2. 프로젝트 후기

- 웹사이트에 흔히 볼 수 있는 카카오로그인을 직접 설정해보면서 어떤 과정을 거치는지 학습할 수 있었고 다음 프로젝트 때 적용한다면 더 빠르게 구현해볼 수 있을 것 같습니다.

 

- 순수 Css와 리액트 프로젝트에서의 반응형 적용하는 원리는 비슷하지만 emotionCss 라이브러리에 알맞게 적용해보았습니다. 반복되는 코드는 변수에 담고 이를 불러와서 사용하면서 코드량은 줄어들고 더 효율적으로 반응형을 구현할 수 있었습니다.

 

- CRUD 중 Create기능을 집중적으로 해보면서 여러 데이터를 한 번에 서버로 전달하는 방법을 학습할 수 있었습니다. 대부분의 웹사이트에서 사용하는 기능이라 생각하여 이 프로젝트에서 가장 중요한 영역이라고 생각합니다.

 

- Create페이지에서 muiDatePicker와 antd라이브러리 등 잘 만들어진 ui를 적용해보면서 실제 업무에서 납기기간이 충분하지 않을 경우 이런 부분에서 빠르게 적용할 수 있음을 학습할 수 있었습니다. 

 

- 벡엔드 api가 다 만들어지기 전까지 프론트엔드에서 계속 기다릴 수 없으므로 디자인 시안을 토대로 임의의 목업데이터를 만들어 미리 ui를 구현했고 실제 api를 전달받았을 때 상이한 영역은 빠르게 수정하여 ui를 구현할 수 있었습니다. 위에서도 언급 했듯이 실제 업무에서 api를 늦게 전달받을 수도 있으므로 선 작업 해보는 과정은 좋은 학습이었습니다.

 

- 쇼핑몰이나 여러 웹사이트에서 빠르게 데이터를 찾는 필터링 기능을 학습할 수 있었습니다. 중첩 필터링 기능을 학습해보면서 filter메소드 내부 로직 원리를 이해할 수 있었습니다.

 

- 마지막으로 프로젝트를 진행해보면서 단순히 ui를 구현하는 것은 조금이나마 익숙해졌다고 생각하지만 좀 더 설계하는 영역, 전체를 볼 수 있는 부분에서는 부족하다는 것을 느낍니다. 다음 프로젝트에서는 더 개선해볼 수 있도록 하겠습니다.

 

react, typeScript를 활용한 클론 프로젝트를 진행했습니다.

react를 사용한 홈페이지 구조를 갖춘 프로젝트는 처음이었기에 시간은 다소 사용되었지만 대략적인 프로젝트 진행과정과 구조, 네트워크 통신 등 많은 부분을 학습할 수 있었던 시간이었습니다.

 

https://github.com/seongEun95/cheonjiam

 

GitHub - seongEun95/cheonjiam

Contribute to seongEun95/cheonjiam development by creating an account on GitHub.

github.com

 

1. 새롭게 경험한 점

1) 공통 컴포넌트 분리하기

- 웹사이트에서 모든 페이지에 사용되는 영역인 Header, Navigation, Footer를 컴포넌트로 만들어  모든페이지에서 사용할 수 있도록 했다.

- 본 사이트가 제품을 판매하는 사이트이므로 하나의 제품 컴포넌트를 만들어 여러 페이지에서 사용할 수 있도록 컴포넌트 제작 했다.

 

* 어려웠던 점

- 기존 회사에서 작업할 때에는 컴포넌트 분리라는 개념이 없었기에 구조화하는 단계가 없이 바로 코드를 작성하는 과정을 진행되었었다. 그래서 그 습관으로 인해 리액트 라이브러리를 사용함에도 컴포넌트 설계를 하기도전에 코드부터 작성하는 과오를 경험했다. 이번 프로젝트를 통해 컴포넌트에 대한 생각과 고민의 과정을 거쳤고 다음 프로젝트에서는 무작정 코드부터 작성하는 것이 아닌 재사용될 컴포넌트를 먼저 구상을 하고 코드 작성을 진행해야겠다고 생각했다.

 

 

2) 네트워크 통신

- 네트워크 통신을 위해 axios 라이브러리를 사용하여 통신했다.

- 프론트엔드에서 임시로 만든 목업데이터 또는 jsonPlaceHolder의 가짜 데이터가 아닌 실제 사이트 제품 api를 받아 데이터를 직접 화면에 렌더할 수 있어서 의미가 있었다.

- 회원가입 시 POST방식으로 email과 password를 전달하며 이때 비밀번호의 경우 보안을 위해 해시 알고리즘인 SHA256을 사용하여 서버로 전달하였다.

- 로그인 시 POST방식으로 이메일과 비밀번호를 전달하였고 그 결과 값인 access token과 refresh token을 로컬스토리지에 저장하여 인증과 인가를 구현할 수 있었다.

- 인증과 인가에 대한 개념은 별도로 노션에 정리하여 학습을 진행했다.

(https://jumbled-carrot-837.notion.site/04ae1803da8740c493129532a155862b?pvs=4)

- 로그인과 회원가입하는 과정에서 통신 오류 발생 시 catch에서 에러 메시지를 확인하여 각각의 메시지에 따라 사용자에게 팝업으로 오류내용에 대해 안내를 진행했다.

- 유저가 입력하는 값들(이메일, 비밀번호)은 클라이언트에서 유효성 검사하는 과정을 넣었다. 유효성 검사는 정규표현식을 사용하여 입력값이 틀렸다면 입력 인풋창 아래에 메시지를 넣어 유저에게 올바른 값을 입력할 수 있도록 안내하였다.

 

* 어려웠던 점

- 실제 프론트엔드와 벡엔드 통신에 대한 경험이 적다보니 머리에 개념 잡기가 힘들었고 코드를 작성하는 것도 익숙하지 않아 검색에 도움을 많이 받았습니다. 이번에 포스트맨으로 미리 테스트를 해보고 실제 axios라이브러리를 통해 통신을 해보면서 실제 업무에서도 이런식으로 하겠구나를 간접적으로 경험해볼 수 있었습니다.

 

 

3) UI 구현

- 사이트 내 가격의 범위를 슬라이더로 구현하는 부분이 어려웠던 부분이었다. 처음에 drop, drag 이벤트로 구현했으나 잔상이 남는 치명적인 오류가 있어 완성하지 못했다. 멘토님의 도움을 받아 해결점을 찾을 수 있었으나 굉장히 난이도가 높게 느껴졌다.

- 메인페이지의 이미지 슬라이드는 react-mulit-carousel 라이브러리를 사용하여 쉽게 구현할 수 있었다.

- 메인페이지 슬라이드 아래 제품 슬라이드는 라이브러리 사용하지 않고 클릭이벤트와 css의 transform translate 속성을 이용하여 직접 만들어보는 경험을 할 수 있었다.

 

 

4) 전역상태관리

- 리액트 전역상태관리는 redux-toolkit을 사용하여 진행했다. 제품을 판매하는 사이트이다보니 장바구니에 대한 부분을 전역상태로 관리하여 어느 페이지에서든 장바구니 기능을 사용할 수 있도록 하였다. 

 

* 어려웠던 점

- 아직 redux에 대해 익숙하지 않아 많은 검색을 통해 찾아보며 진행하였다. 그리고 같은 제품을 담았을 때는 제품의 숫자만 추가되고 장바구니 페이지 내에서도 체크된 제품에 대해서만 가격을 적용하는 등의 세세한 기능들이 많아서 까다로웠던 작업이었다. 

 

 

2. 앞으로 해야할 점

- 코드를 작성하기 전 컴포넌트 설계를 먼저 진행하고 실제 페이지를 구성할 때에는 조립하는 식으로 진행해야한다.

- 벡엔드와 통신하는 과정이 익숙하지는 않기에 좀 더 많은 프로젝트를 통해 능숙하게 할 수 있도록 연습해야한다.

- 전역상태관리도 어떤 데이터를 전역으로 관리할 것인지를 결정할 수 있는 판단과 좀 더 익숙하게 사용할 수 있도록 연습해야한다.

웹 사이트를 사용하다보면 회원가입을 해야 서비스를 이용할 수 있는 경우들이 있습니다.

예를 들어 넷플릭스나 티빙 등의 *OTT(Over The Top)를 이용하는 경우 회원가입을 해야 영상 목록을 확인할 수 있고 또한 회원가입 후 멤버십 가입을 해야 영상 시청을 할 수 있는 경우가 대다수입니다.

* OTT(Over The Top)는 인터넷을 통해 볼 수 있는 TV 서비스를 일컫는다.

 

웹 사이트에 회원가입을 하는 경우 자신의 정보를 입력해야 회원가입을 진행할 수 있습니다.

예시 OTT 회원가입 창

회원가입 후 서비스를 이용할 수 있지만 사용자 입장에서 회원가입은 번거로운 작업일 수도 있습니다.

만약 이미 회원가입 된 사이트의 정보를 이용하여 따로 입력폼을 작성하지 않고 회원가입이 된다면 매우 편리하게 이용할 수 있을 것입니다. 

 

실제로 제가 사용하는 아래 예시 사이트에선 다양한 로그인 방법을 제공하고 있습니다.

다양한 로그인 방법

저는 이미 네이버에 회원가입이 되어 있고 "네이버로 시작하기" 버튼을 통해 네이버 정보 제공에 동의 시 따로 회원가입 폼을 작성하지 않더라도 서비스를 이용할 수 있습니다.

 

아래 내용부터는 다양한 개발환경 중 Vue.js에서 기본적인 방법으로 네이버 로그인 구현에 대해 작성하고자 합니다.

 


- 목차 -

가. NAVER Developers 세팅

나. Vue에서 로그인 코드 세팅

다. 참고자료


가. NAVER Developers 세팅

1. NAVER Developers 접속 후 우측 상단 로그인 버튼을 클릭, 로그인까지 진행합니다.

https://developers.naver.com/products/login/api/api.md

NAVER Developers 초기 접속 화면

 

2-1. 네이버 Application을 처음 이용하는 경우 약관동의, 계정 정보 등록, 애플리케이션 등록까지 진행해야 합니다.

① 상단 메뉴 중 Application을 클릭합니다.

② 이용약관을 확인 후 동의합니다.

③ 확인버튼을 누릅니다. 

2-2. 계정 정보 등록

① 휴대폰 인증을 진행합니다.

② 체크박스를 클릭합니다.

③ 위 진행 시 버튼이 활성화되고 확인을 클릭합니다.

계정 정보 등록

2-3. 애플리케이션 등록

① 자신의 애플리케이션 이름을 작성합니다. (저는 테스트로 진행하므로 임의로 작성했습니다)

② 사용 API, "선택하세요." 를 클릭하여 네이버 로그인을 클릭합니다.

애플리케이션 등록

③ 네이버 로그인을 클릭하면 제공 정보를 선택할 수 있습니다. 사용자에게 필수로 받을 정보라면 필수쪽 체크박스에 체크하고 선택적으로 받을 정보라면 추가쪽의 체크박스를 체크합니다.

 

네이버로그인 필수, 선택 정보

* 아래 이미지에서 필수에 체크된 항목은 필수 제공 항목으로 되며 추가에 체크된 항목은 추가 제공 항목에 추가됩니다.

네이버 필수 제공항목, 추가 제공 항목 예시

 

④ "환경 추가" 를 누른 뒤 "PC 웹"을 클릭합니다.

⑤ 서비스 URL을 입력합니다. (아래 이미지에서는 vue 로컬 주소를 입력했습니다)

⑥ Vue 라우터 path가 추가된 URL을 입력합니다.

⑦ "등록하기"를 클릭하면 NAVER Developers의 네이버 로그인 세팅이 완료됩니다.

환경 추가

2-4 멤버관리에 주소 추가

① 위 과정을 거치면 아래 화면이 나옵니다. 메뉴 중 멤버관리를 클릭합니다.

② 테스트할 아이디를 등록합니다. (관리자 ID 등록 시 테스트 ID에 따로 등록하지 않아도 관리자 ID로 테스트가 가능합니다)

나. Vue에서 로그인 코드 세팅

1. 터미널에서 vue 프로젝트를 생성합니다.

2. 네이버 로그인 화면 파일을 생성합니다.

3. /router/index.js, path에 /login/naver 로 라우터를 작성합니다.

// /router/index.js, 라우터 작성
{
    path: '/login/naver',
    name: 'naverLoginView',
    component: () =>
      import(
        /* webpackChunkName: "login", webpackPrefetch:true */ '../views/0_login/naverLoginView.vue'
      )
  }

4. /public/index.html, head태그 안에 script (sdk 주소가 추가된) 태그를 아래와 같이 추가합니다.

<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
  <link rel="icon" href="<%= BASE_URL %>favicon.ico">
  <title>
    <%= htmlWebpackPlugin.options.title %>
  </title>
  <script src="https://static.nid.naver.com/js/naveridlogin_js_sdk_2.0.2.js"></script> // sdk 코드 추가
</head>

5. 아래 주소의 SDK 다운로드로 접속하면 javascript용 네이버 로그인 라이브러리의 샘플이 존재하지만 Vue를 이용하므로 아래 6번의 코드를 작성합니다.

https://developers.naver.com/docs/login/sdks/sdks.md

 

6. 네이버 로그인 View.vue 파일 template에 전체 코드를 아래와 같이 추가합니다.

- clientId 와 callbackUrl은 naver Developers의 application 메뉴에서 client Id 확인이 가능하며 콜백  URL은 설정 시 작성했던 주소를 입력합니다

(콜백 URL 예시 : http://localhost:8080/login/naver, 콜백 URL을 기억하지 못 한다면 NAVER Developers의 Application메뉴에서 확인이 가능합니다.)

// NaverLoginView.vue
<template>
  <div>
    <div id="naverIdLogin">
      <a id="naverIdLogin_loginButton" href="#" role="button"
        ><img src="https://static.nid.naver.com/oauth/big_g.PNG" width="320"
      /></a>
    </div>
    <button @click="naverLogout($event)">로그아웃</button>
  </div>
</template>
<script>
export default {
  /* eslint-disable */
  components: {},
  data() {
    return {
      nickName: '',
      naverLogin: null
    }
  },
  setup() {},
  created() {},

  mounted() {
    this.naverLogin = new window.naver.LoginWithNaverId({
      clientId: 'client Id', // client id 작성
      callbackUrl: 'http://localhost:8080/login/naver', // call back url 작성
      isPopup: true,
      loginButton: { color: 'green', type: 3, height: 60 }
    })
    /* 네아로 로그인 정보를 초기화하기 위하여 init을 호출 */
    this.naverLogin.init()

    /* 현재 로그인 상태를 확인 */
    this.naverLogin.getLoginStatus((status) => {
      if (status) {
        console.log(this.naverLogin.user.getNickName()) // 제공 정보 중 닉네임에 대한 정보 출력
        this.nickName = this.naverLogin.user.getNickName()
      }
    })
  },
  unmounted() {},
  methods: {
    naverLogout(event) {
      event.preventDefault()
      this.naverLogin.logout()
    }
  }
}
</script>

* [eslint] 오류 발생 시 mounted()안에 /* eslint-disable */을 작성하여 eslint규칙을 무시할 수 있습니다.

Eslint 오류

 

7. 정상적으로 작성되었다면 아래 네이버 로그인 버튼이 나오고 클릭 시 네이버 로그인 팝업이 등장합니다.

네이버 아이디로 로그인 버튼

8. 동의항목을 클릭하고 "동의하기"버튼을 누르면 콘솔창에 닉네임이 출력됩니다. (정상 로그인)

// mounted() 코드 일부분
console.log(this.naverLogin.user.getNickName()) // 닉네임 출력

 

9. 화면상의 로그아웃 버튼을 누르고 새로고침 시 콘솔창에 닉네임이 출력이 안 된다면 정상적으로 로그아웃된 상태가 됩니다.

 

 

10. NickName을 제외한 다른 값을 받고 싶다면 아래 주소로 접속합니다. (네이버 로그인 개발 가이드)

https://developers.naver.com/docs/login/devguide/devguide.md#3-4-5-%EC%A0%91%EA%B7%BC-%ED%86%A0%ED%81%B0%EC%9D%84-%EC%9D%B4%EC%9A%A9%ED%95%98%EC%97%AC-%ED%94%84%EB%A1%9C%ED%95%84-api-%ED%98%B8%EC%B6%9C%ED%95%98%EA%B8%B0

 

네이버 로그인 개발가이드 - LOGIN

네이버 로그인 개발가이드 1. 개요 4,200만 네이버 회원을 여러분의 사용자로! 네이버 회원이라면, 여러분의 사이트를 간편하게 이용할 수 있습니다. 전 국민 모두가 가지고 있는 네이버 아이디

developers.naver.com

* 출력결과 필드 항목에 response/nickname 이라면 user.getNickName() 의 형식으로 하여 값들을 받아올 수 있습니다.

console.log(this.naverLogin.user.getNickName()) // 닉네임
console.log(this.naverLogin.user.getId()) // Id
console.log(this.naverLogin.user.getName()) // 이름
console.log(this.naverLogin.user.getEmail()) // 이메일
console.log(this.naverLogin.user.getGender()) // 성별
console.log(this.naverLogin.user.getAge()) // 나이
console.log(this.naverLogin.user.getBirthday()) // 생일 (년도 제외)

 


위와 같이 서비스를 운영할 때 자체 회원가입도 필요하지만 네이버와 구글과 같은 이미 많은 사용자가 있는 플랫폼의 로그인 기능들을 포함한다면 사용자에게 더 좋은 선택지를 제공할 수 있으며 간편하게 회원가입을 진행할 수 있게 됩니다.

 

해당 글은 vue.js 환경에서 로그인 구현에 대해 알아보았습니다.

https://developers.naver.com/docs/login/devguide/devguide.md#%EB%84%A4%EC%9D%B4%EB%B2%84-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EA%B0%9C%EB%B0%9C%EA%B0%80%EC%9D%B4%EB%93%9C

 

네이버 로그인 개발가이드 - LOGIN

네이버 로그인 개발가이드 1. 개요 4,200만 네이버 회원을 여러분의 사용자로! 네이버 회원이라면, 여러분의 사이트를 간편하게 이용할 수 있습니다. 전 국민 모두가 가지고 있는 네이버 아이디

developers.naver.com

네이버 로그인에 더 필요한 정보가 있다면 위의 링크(네이버 로그인 개발가이드)를 확인하시면 됩니다.

 

저의 글을 읽어주셔서 감사합니다.


다. 참고자료

1. https://www.youtube.com/c/개발자의품격 - 부트캠프,  Vue.js 네이버 로그인

2. https://developers.naver.com/products/login/api/api.md - NAVER Developers, 네이버 로그인

3. https://developers.naver.com/docs/login/overview/overview.md - NAVER Developers, 네이버 로그인, 개요

4. https://terms.naver.com/entry.naver?docId=3579352&cid=59088&categoryId=59096 - NAVER 지식백과, OTT

먼저 vue.js란 자바스크립트 프레임워크로 SPA(Single Page Application)을 구현하는데 최적화되어 있습니다.

SPA의 장점으로 단일 페이지로 구성되어 있기 때문에 페이지 내에 메뉴를 클릭하여 이동하더라도 새로고침이 진행되지 않아 사용자에게 좋은 이용 경험을 제공해줄 수 있습니다.

즉, 하나의 페이지에서 메뉴 이동시 전체 페이지가 변경되는 것이 아니라 변경이 필요한 부분만 교체됩니다.

 

SPA의 장점도 존재하지만 처음 페이지를 접속할 때 다른 페이지들의 소스들도 한 번에 로드되기 때문에

처음 페이지 접속 시 느리다는 단점도 존재합니다.

 

vue의 라우터 방식을 통해 javaScript파일들을 미리 캐시에 담아 최적화된 웹 페이지를 제공한다면 위의 단점을 보완할 수 있게 됩니다.

 

SPA의 단점을 보완할 수 있는 vue 라우터 방식에 대해서 알아봅시다.


- 목차 -

가. 라우터 기본 구조

나. 라우터 방식 3가지

 1. app.js 파일에 포함되는 방식

 2. app.js가 아닌 별도의 js파일로 분리되는 방식

 3. webpackPrefetch:true가 포함된 방식

다. 참고자료

 


가. 라우터 기본 구조

vue를 vscode 터미널로 초기 설치 시 아래 이미지와 같은 폴더 구조로 되어있습니다.

이 중 src폴더 - router폴더 안 index.js를 클릭하셔서 확인하시면 됩니다.

vue 폴더 구조

 

index.js 파일의 routes 배열 코드는 아래와 같습니다.

// index.js 코드의 일부
import HomeView from '../views/HomeView.vue' // HomeView
const routes = [
  {
    path: '/', // URL 경로
    name: 'home', // 유일한 값
    component: HomeView // 실제 컴포넌트 경로
  }
]

1. path

App.vue의 <router-link to="경로">에서의 경로를 의미합니다. 실제로 url 경로를 입력하면 해당 페이지로 이동합니다.

만약 path : "/about" 이고 <router-link to="/about"></router-link>이라면

Home에서 주소창에 "/about" 경로를 입력하게 되면 about페이지로 이동하게 됩니다.

* router-link는 컴파일 시 a 태그로 변환됩니다. to 경로는 href 속성으로 변환됩니다.

about 페이지 주소

 

2. name

페이지가 작성되고 메뉴가 추가되는데, name은 브라우저의 독립적인 url로 접근 가능한 모든 페이지를 의미합니다. 계속 메뉴가 추가되더라도 동일한 이름이 있으면 안 되며 유일한 값만 가져야 합니다.

 

3. component

path로 이동했을 때 연결할 페이지 컴포넌트(확장자 : .vue)에 대한 정보를 입력합니다.

위의 코드에서 컴포넌트 경로가 '../views/HomeView.vue' 으로 되어 있으므로

Home 메뉴로 이동하면 해당 경로의 vue컴포넌트가 로드됩니다.


나. 라우터 방식 3가지

라우터 방식에는 3가지 방식이 있습니다.

라우터 방식에 따라 적절하게 사용된다면 전체 앱의 사용 속도를 최적화할 수 있게 됩니다.

 

1. app.js 파일에 포함되는 방식

// index.js 파일
import HomeView from '../views/HomeView.vue'
const routes = [
  {
    path: '/',
    name: 'home',
    component: HomeView
  }
]

1번 방식은 routes 배열 밖에 별도의 import 코드가 존재하며 routes 안 component에 import 명만 넣는 형태입니다.

 

웹 개발이 완료되고 vue 컴포넌트들이 컴파일되면 브라우저에서 이해할 수 있는 javaScript 코드들로 변환됩니다.

이때 라우터 1번 방식으로 작성된 파일들은 app.js 파일에 포함됩니다.

 

app.js는 첫 화면이 로드될 때 같이 실행되므로

사용자가 무조건 해당 페이지에 접속하는 경우 1번 방식을 사용하게 됩니다.

첫 화면 로드 시 app.js는 같이 로드

* chunk-vendors.js는 개발한 코드를 제외하고 참조하고 있는 외부 js라이브러리 스크립트 코드가 포함됩니다.

 

 

2. app.js가 아닌 별도의 js파일로 분리되는 방식

// index.js 파일
const routes = [
  {
    path: '/about',
    name: 'about',
    component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
  }
]

2번 방식은 routes의 component 함수 안에 import로 작성된 방식입니다.

 

/* webpackChunkName : "about" */

여기에 어떤 이름을 지정하는가에 따라 페이지에 접속했을 때 지정한 이름의 js파일로 로드됩니다. 위의 코드의 결과로는 아래와 같습니다.

about.js 로드

2번 방식은 about 페이지로 이동되는 순간에 서버로부터 about.js파일을 불러오게 됩니다.

app.js 파일과 분리되어 생성됩니다.

 

아래 3번 방식의 경우 방문할 페이지의 js소스를 미리 캐시에 담아 해당 페이지에 접속했을 때 캐시에 있는 js파일이 실행되면서 더 빠르게 화면이 로드됩니다.

 

반면 2번 방식은 이동할 페이지에 방문할 가능성이 낮거나 파일 용량이 가벼운 경우

첫 번째 페이지 접속 시 js파일을 캐시에 담아 첫 화면 로드 시간을 늘릴 이유가 없으므로 2번 방식을 사용할 수 있습니다.

 

 

3. webpackPrefetch:true가 포함된 방식

const routes = [
  {
    path: '/about',
    name: 'about',
    component: () =>
      import(
        /* webpackChunkName: "about", webpackPrefetch:true */ '../views/AboutView.vue'
      )
  }
]

3번 방식은 기존 2번 방식 webpackChunkName : "about" 오른쪽에 ,(콤마) webpackPrefetch:true가 추가된 방식입니다.

 

webpackPrefetch:true가 포함된 페이지에 접속 시 네트워크 탭에서 (prefetch cache)가 표시됩니다.

 

(prefetch cahce)

 

그리고 개발자 창 코드에서도 <head> 태그 안에 <link rel="prefetch" as="script" href="/js/about.js"> 코드를 확인할 수 있습니다.

 

link rel prefetch

link rel="prefetch"은 사용자가 요청할 가능성이 높은 페이지의 리소스를 미리 캐시에 담아 다음 요청이 빨리 이루어질 수 있도록 합니다.

 

따라서 3번 방식은 사용자가 방문할 가능성이 높거나 용량이 높은 페이지인 경우 소스들을 미리 캐시에 가져오고

해당 페이지 이동 시점에 캐시에 있는 소스를 가져와 빠르게 화면을 로드할 수 있도록 합니다.

 

* javascript는 파서 차단 리소스 입니다.

브라우저 렌더링이 진행될 때 javascript를 만나게 되면 html 페이지 파싱이 중단됩니다.

html 파싱이 중단되면 javascript 분석이 진행되는 동안 웹 화면이 빈 화면으로 유지됩니다.

이런 경우 사용자는 웹 사이트가 느리게 느껴질 수도 있습니다.

하지만 prefetch로 가져온 리소스는 캐시에 저장만 하고 스크립트를 해석하지 않기 때문에 브라우저 렌더링을 중단하지 않습니다.

따라서 서버에서 javascript 파일은 가져오지만 해석하지 않고 렌더링을 차단하지 않기 때문에 성능에 이점이 발생하게 됩니다.

 


 

라우터 방식 3가지를 정리하면

1. app.js 파일에 포함되는 방식은 사용자가 무조건 접속하게 되는 메뉴의 페이지인 경우 사용합니다.

2. app.js 파일이 아닌 별도의 js파일로 분리되는 2번 방식은 사용자가 방문할 가능성이 적거나 용량이 가벼운 경우 사용하게 됩니다.

3. webpackPrefetch:true로 미리 캐시에 담아두는 방식인 3번은 사용자가 방문할 가능성이 높거나 페이지 용량이 무거운 경우 미리 캐시에 담아 접속 시점에 페이지 로드를 빠르게 할 수 있도록 합니다.

 

위의 3가지 방식을 알맞게 사용하여 전체 페이지 설계를 진행하면 SPA 단점의 보완과 웹 페이지 로드 시간을 최적화할 수 있게 됩니다.

 


다. 참고자료

1. https://www.youtube.com/c/개발자의품격 - 부트캠프, Vue.js 라우터 이해하기

2. https://v3.router.vuejs.org/kr/guide/ - Vue Router, 시작하기

3. http://www.tcpschool.com/html-tag-attrs/link-rel - TCPSCHOOL, <link> 태그의 rel 속성

'언어 > Vue.js' 카테고리의 다른 글

[Vue.js] Vue.js에서 네이버 로그인 구현  (1) 2022.11.12

웹 스토리지는 클라이언트에서 서버로 데이터를 전송하여 실제 데이터베이스에 저장하는 것이 아닌 웹 브라우저의 저장공간에 데이터를 저장하게 됩니다.

(저장 공간 : 5mb)

 

웹 스토리지는 보안상 중요한 정보인 로그인 비밀번호 등엔 사용되지 않으며 비교적 덜 중요한 정보인 장바구니 목록이나 테마 정보 등에 사용됩니다.

 

웹 스토리지엔 localStorage와 sessionStorage가 있습니다.

각 스토리지는 키와 값으로 저장되며 사용법은 동일하지만 삭제 시점의 차이가 존재합니다.

 

웹 브라우저 저장소인 localStorage와 sessionStorage에 대해 알아보고자 합니다.

 


 

- 목차 - 

가. localStorage & sessionStorage

  1. localStorage

  2. sessionStorage

나. 사용법

  1. 기본 사용법

  2. 배열 저장법

다. 참고자료

 


 

가. localStorage & sessionStorage

1. localStorage

- 브라우저를 닫거나 종료하여도 별도로 삭제하기 전까지는 데이터가 남아있습니다.

- 로컬스토리지는 현재 사용하는 기기(PC, 스마트폰)에 저장하며 도메인별로 따로 저장됩니다.

(pc에서 로컬스토리지를 저장했다면 스마트폰에서는 해당 데이터를 확인할 수 없습니다)

(www.naver.com에 저장한 데이터는 www.daum.net에서 확인할 수 없습니다. 하지만 도메인이 같을 경우 새 창을 띄워도 동일한 데이터를 확인할 수 있습니다)

 

 

2. sessionStorage

- 브라우저를 닫거나 종료하면 데이터가 삭제됩니다.

- 세션스토리지는 탭별로 별도의 데이터를 저장하게 됩니다.

(같은 브라우저 탭 안에서만 데이터가 유지됩니다)

 

 

 

* localStorage & sessionStorage 차이점

- 아래 코드를 실행하여 로컬스토리지와 세션스토리지에 데이터를 각 하나씩 저장합니다.

// storage.html 파일
localStorage.setItem("local", "local_value")
sessionStorage.setItem("session", "session_value")

- 다른 html 파일을 생성하여 위의 데이터를 console에 출력합니다.

// storage02.html 파일
console.log(localStorage.getItem("local")) // local_value 출력
console.log(sessionStorage.getItem("session")) // session_value 출력

실제 콘솔창

위 이미지처럼 실제로 콘솔창에는 2개의 값이 출력됩니다.

 

- 새 탭을 열어 위와 동일한 주소로 접속합니다.

session 값은 null로 출력

- 그 결과 다른 탭에서 sessionStorage 값은 null(값 없음)이 출력되고 localStorage 값만 출력되고 있습니다.

- 브라우저를 완전히 종료하고 다시 실행해도 위와 같은 결과가 도출됩니다.

 

 


 

나. 사용법

1. 기본사용법

1. 먼저 localStorage와 sessionStorage는 html5부터 지원하기 때문에 아래 코드를 통해 지원여부를 확인할 수 있습니다.

if (typeof (Storage) !== "undefined") { // 스토리지 지원 여부 확인
      // typeof(Storage)가 undefined가 아니면 웹 스토리지를 지원합니다.
    } else {
      // 웹 스토리지를 지원하지 않습니다.
    }

 

 

 

2. localStorage(sessionStorage)에 데이터 추가하기

* localStorage와 sessionStorage는 사용법이 동일하기 때문에 코드 예제엔 localStorage가 사용되지만 sessionStorage를 사용시 이름만 바꿔서 사용하시면 됩니다.

// 첫 번째 파라미터 사용할 키 명, 두 번째 파라미터 값
localStorage.setItem("key", "value");

localStorage객체의 내장함수인 setItem()을 사용하여

첫 번째 파라미터로 키 명을 작성하고

두 번째 파라미터로 값을 작성합니다.

 

 

 

3. localStorage(sessionStorage)에 데이터 가져오기

console.log(localStorage.getItem("key")) // value가 콘솔에 출력됩니다.

localStorage객체의 내장함수인 getItem("키 명")을 사용합니다.

파라미터로 setItem()으로 추가한 데이터의 키 명을 입력합니다.

 

 

* setItem()으로 추가한 데이터를 console.log()로 확인하지 않아도 브라우저에서 확인이 가능한 방법

- 로컬스토리지를 저장한 브라우저를 실행합니다. (vscode의 라이브서버 확장기능에서도 확인할 수 있습니다)

- 키보드 상단에 F12키를 누르시거나 마우스 오른쪽 클릭 후 검사 항목을 클릭합니다.

 

마우스 오른쪽 클릭 후 검사 항목 클릭

 

- 검사를 클릭하면 개발자도구창이 실행됩니다.

개발자 도구창을 가로로 늘리시고 상단에 응용 프로그램을 클릭합니다.

그리고 좌측에 로컬 저장소와 세션저장소를 확인할 수 있습니다.

 

개발자 도구의 응용 프로그램 및 로컬, 세션 저장소 확인

 

- 로컬 저장소 좌측에 ▶ 을 클릭하면 아래 저장된 주소가 나오고 주소를 클릭하면

아래 이미지와 같이 setItem("key, "value")으로 저장한 데이터를 확인할 수 있습니다.

 

웹 브라우저에서 로컬 스토리지 키 값 확인 가능

 

 

 

 

 

4. localStorage(sessionStorage)에 데이터 삭제하기

// 키가 key인 데이터를 삭제합니다.
localStorage.removeItem("key");

localStorage객체의 내장함수인 removeItem("키 명")을 사용합니다.

파라미터로 setItem()으로 추가한 데이터의 키 명을 입력합니다.

실제로 실행한 결과 아래 이미지와 같이 추가한 key 데이터가 삭제된 모습을 볼 수 있습니다.

 

로컬 스토리지 키 값이 비어있는 모습

 

 

 

5. localStorage(sessionStorage)에 모든 데이터 삭제하기

// 로컬 스토리지 내의 모든 데이터를 삭제합니다.
localStorage.clear()

localStorage객체의 내장함수인 clear()를 사용합니다.

실제로 삭제되는지 확인하기 위해 아래 별도의 데이터를 추가하였습니다.

 

clear()를 테스트하기 위해 별도의 데이터 추가

 

그리고 clear() 실행 시 아래와 같이 모든 데이터가 한 번에 삭제되었습니다.

 

모든 데이터가 삭제된 모습

 

 

 

 

2. 배열 저장법

로컬스토리지와 세션스토리지는 키 값으로 문자열만 저장되기 때문에 바로 배열을 넣게되면 배열이 아닌 문자열로 저장됩니다.

// 두 번째 파리미터로 배열을 추가했으나 문자열로만 추가됩니다.
localStorage.setItem('key', ["apple", "banana", "pear"])

배열로 저장했으나 문자열로 저장된 모습

두 번째 파라미터를 배열로 지정했으나 값에는 위 이미지와 같이 배열이 아닌 문자열로 추가됩니다.

 

 

 

* JSON.stringify() & JSON.parse() 이용하기

let arr = ["apple", "banana", "pear"]
localStorage.setItem('key', JSON.stringify(arr)) // ["apple", "banana", "pear"]로 추가

값에 배열로 추가가 된 모습

setItem() 내장함수의 두 번째 파라미터로 JSON.stringify()를 이용해 문자열로 변환 후 전달 시 배열 형태로 저장됩니다.

 

하지만 getItem()으로 가져올 때 그냥 가져오게 되면 배열이 아닌 문자열 형태로 출력됩니다.

따라서 가져올 때 JSON.parse()를 이용해 가져오게 되면 배열로 사용할 수 있게 됩니다.

console.log(JSON.parse(localStorage.getItem("key"))) // 배열 형태로 출력

실제 콘솔창에 배열로 출력된 모습

위 코드를 실행 시 콘솔 창에 배열로 출력된 모습을 확인할 수 있습니다.

 


웹 브라우저 저장소인 localStorage와 sessionStorage를 알아보았습니다.

둘의 가장 큰 차이점은 삭제 시점에 있었는데요,

localStorage는 삭제하기 전까지 웹 브라우저를 종료하여도 데이터가 남아있으며 동일한 도메인 내에서는 데이터를 공유할 수 있음을 알 수 있었습니다.

하지만 sessionStorage는 동일한 도메인에서도 새 탭에선 데이터를 공유할 수 없으며 브라우저를 종료하면 데이터가 삭제됨을 알 수 있었습니다.

 

둘의 차이점을 확인하여 적절한 상황에 웹 브라우저 저장소를 사용하는 것이 중요하다고 생각되어 집니다.

여기까지 저의 글을 읽어주셔서 감사합니다.


 

3. 참고자료

1. https://www.youtube.com/watch?v=nmLwQyWzkXU - 개발자의 품격, 자바스크립트 기초-localStorage와 sessionStorage 이해하기

2. http://www.tcpschool.com/html/html5_api_webStorage - Web Storage

웹 개발에서 프론트엔드 개발자와 벡엔드개발자는 서로  협업하여 일을 진행합니다.

프론트엔드에서는 화면을 구성하기 위해 서버에서 데이터가 오면 그 데이터를 가지고 화면에 출력하게 됩니다.

 

하지만 프론트엔드와 벡엔드가 개발하는 과정에서 속도 차이가 발생할 수 있으며 벡엔드에서 API 주소가 올 때까지 프론트는 대기하게 되는 상황에 직면할 수도 있습니다.

 

이때 가상의 웹 서버를 구동할 수 있게하는 오픈 소스 모듈인 json-server를 이용하면 위 문제를 해소할 수 있게 됩니다.

json-server를 사용하게 되면 실제 서버가 없더라도 실제와 굉장히 유사한 서버를 사용할 수 있게되어 미리 코드를 구현할 수 있게 됩니다.

프론트엔드에서 중요하게 사용되는 json-server 사용하는 방법에 대해서 알아보고자 합니다.

 

- 목차 - 

가. json-server 설치 방법

나. json-server 사용 방법

 * fetch api

다. 참고자료

 


 

가. json-server설치 방법

* json-server를 설치하기 위해 아래 글은 visual studio code 에디터를 사용합니다. 아래 사이트에서 미리 다운로드 부탁드립니다.

https://code.visualstudio.com/

 

Visual Studio Code - Code Editing. Redefined

Visual Studio Code is a code editor redefined and optimized for building and debugging modern web and cloud applications.  Visual Studio Code is free and available on your favorite platform - Linux, macOS, and Windows.

code.visualstudio.com

 

1. 프로젝트 폴더를 만들고 폴더 안에 json-server 설치할 폴더까지 생성합니다.

프로젝트 폴더 생성
json-server 설치할 폴더 생성

 

 

 

2. visual studio code를 실행하고 Open Folder 버튼을 눌러 프로젝트 폴더를 불러옵니다.

 

visual studio code를 실행 후 Open Folder 버튼을 클릭

 

 

 

 

3. 상단 메뉴 Terminal에서 new Terminal을 클릭합니다.

 

상단 메뉴 Terminal - New Terminal

 

4. 클릭하면 에디터 아래에 Terminal창이 열립니다. 설치폴더로 이동하기 위해 cd json-server를 입력하고 엔터키를 누릅니다.

* cd는 change directory를 뜻하는 명령어로 현재 작업 폴더를 변경하는 명령어 입니다.

cd json-server 입력 후 엔터

 

 

5. 아래 json server 주소로 접속합니다.

https://www.npmjs.com/package/json-server

 

json-server

Get a full fake REST API with zero coding in less than 30 seconds. Latest version: 0.17.0, last published: a year ago. Start using json-server in your project by running `npm i json-server`. There are 291 other projects in the npm registry using json-serve

www.npmjs.com

 

 

 

6. 스크롤을 내리다보면 Getting started가 있으며 Install JSON Server의 코드를 복사합니다.

* 맥 사용자는 복사한 코드 앞에 sudo를 추가 입력하여 설치합니다.

# 윈도우 사용자
npm install -g json-server

# 맥 사용자
sudo npm install -g json-server

 

7. 에디터로 돌아와서 Terminal창에 복사한 코드를 붙여넣기하고 엔터키를 누릅니다.

 

 

 

8. 설치가 완료되면 json-server 사이트의 설명대로 db.json 파일을 생성합니다.

* 화살표로 가리키는 아이콘이 새파일을 생성하는 아이콘이므로 클릭 후 db.json을 입력하고 엔터를 누르면 파일이 생성됩니다.

db.json 파일 추가

.

 

 

 

9. 그리고 아래 예시 코드를 복사해서 db.json파일에 붙여넣고 저장합니다.

 

{
  "posts": [
    { "id": 1, "title": "json-server", "author": "typicode" }
  ],
  "comments": [
    { "id": 1, "body": "some comment", "postId": 1 }
  ],
  "profile": { "name": "typicode" }
}

 

 

10. 마지막으로 json-server를 구동시키기 위해 터미널에 json-server --watch db.json 명령어를 입력하고 엔터키를 누릅니다.

json 서버 구동 명령어

json-server --watch db.json

 

 

11. 엔터키를 누르고 잠시 뒤에 아래 코드가 나오면 구동까지 완료된 상태입니다.

* http://localhost:3000/posts 는 해당 db.json파일 안 데이터 posts에 접근할 수 있는 리소스 주소를 나타냅니다.

\{^_^}/ hi!

  Loading db.json
  Done

  Resources
  http://localhost:3000/posts // 접근가능한 리소스 주소
  http://localhost:3000/comments // 접근가능한 리소스 주소
  http://localhost:3000/profile // 접근가능한 리소스 주소

  Home
  http://localhost:3000

  Type s + enter at any time to create a snapshot of the database
  Watching...

http://localhost:3000/posts는 db.json에서 posts에 접근하는 리소스 주소 입니다.

 

* 위 주소를 javaScript로 활용하여 데이터 조회, 생성, 수정, 삭제가 가능합니다.

* 위의 db.json파일의 데이터들은 원하는대로 수정해서 테스트할 수 있습니다.

* json-server로 미리 구현 후 최종적으로 벡엔드 api가 완료되면 주소부분만 수정하여 대기시간 없이 구현할 수 있게 됩니다.

 

 


 

나. json-server 사용방법

위에서 json-server를 설치하여 구동까지 완료되었습니다.
아래부터는 json-server를 구동한 상태에서 데이터 조회, 생성, 수정, 삭제를 알아보고자 합니다.

 

fetch api

데이터를 조작하기 위해 다양한 방법 중 fetch api를 사용하고자 합니다.

 

fetch api를 이용하면 필요할 때 서버에 요청을 보내고 정보를 받아오는 일을 할 수 있습니다.
- 첫 번째 파라미터 : 접근할 URL
- 두 번째 파라미터 : 옵션

 

데이터 조회

fetch('http://localhost:3000/posts')
  .then((response) => response.json())
  .then((data) => console.log(data));

위 코드는 fetch api를 이용해 데이터를 조회하는 코드 입니다.

 

- fetch('http://localhost:3000/posts')
데이터를 정상적으로 받아오면 then()안의 함수가 실행됩니다.

- .then((response) => response.json())
fetch는 promise를 포함하고 있어 서버로 받은 응답을 response 파라미터로 받습니다.

response 안에는 응답코드, 응답메시지 등의 정보를 담고 있습니다.


- response.json()은 받아온 json 데이터를 사용할 수 있도록 객체 형태로 변환합니다.
(JSON.parse()와 동일한 기능입니다)

- .then((json) => console.log(json));
json 데이터를 마지막 then()에서 출력합니다.

 

fetch api로 db.json의 posts데이터를 조회한 결과 콘솔창

posts 리소스를 조회한 결과, 위 콘솔창의 0번 인덱스와, 아래 db.json파일의 posts와 동일한 것을 확인할 수 있습니다.

fetch api로 posts데이터를 조회한 결과 db.json의 posts의 데이터와 동일

 

 

데이터 생성

function postData() {
      const data = { title: "javaScript", author: "Mike" }; // 생성할 코드
      fetch("http://localhost:3000/posts", { // 첫 번째 파라미터 : 주소, 두 번째 파라미터 : 옵션
        method: "POST", // 데이터 생성 method : POST
        body: JSON.stringify(data), // body 보낼 데이터 입력 (문자열로 변환 후 전송)
        headers: headers = {
          "content-type": "application/json;charset=UTF-8"  // 데이터 타입 : json, 인코딩 방식 : UTF-8
        },
      }).then((response) => response.json()).then((json) => console.log(json))
    }

위 코드는 fetch api를 이용해 데이터를 생성하는 코드 입니다.

 

- 생성할 데이터를 미리 변수에 담아 놓습니다. (const data = { title: "javaScript", author: "Mike" };)
- 첫 번째 파라미터에 주소가 입력되고,
데이터를 생성하기 위해 두 번째 파라미터로 옵션을 입력해야 합니다.

- 데이터를 생성하기 위해서는 method에 POST를 입력합니다.
- body에는 보낼 데이터를 입력합니다.
- 데이터를 전송하려면 JSON.stringify(data)를 이용해 문자열로 변환 후 전송합니다.
- 생성할 데이터 타입이 json이므로 headers에는 content-type이 application/json이 되고 인코딩 방식은 전 세계 언어를 지원하는 charset=UTF-8을 사용합니다.

 

데이터 생성 결과 콘솔창 : 인덱스 번호 1번이 생성되었습니다.

실제로 코드 실행 시 1번 인덱스에 데이터가 생성된 것을 확인할 수 있습니다.

 

데이터 수정

function putData() {
      const data = { title: "fetch api", author: "john" }; // 수정할 데이터를 변수에 담아놓습니다.
      fetch("http://localhost:3000/posts/2", { // 주소 끝에 변경할 id 값을 입력합니다.
        method: "PUT", // 데이터 수정시 method에 PUT을 입력합니다.
        body: JSON.stringify(data), // 데이터 전달 시 문자열로 변경 후 전송합니다.
        headers: headers = {
          "content-type": "application/json;charset=UTF-8"
        }
      }).then(response => response.json()).then(json => console.log(json))
    }

위 코드는 fetch api를 이용해 데이터를 수정하는 코드 입니다.

 

 

- 수정할 데이터를 미리 변수에 담아 놓습니다. (const data = { title: "fetch api", author: "john" };)
- fetch("http://localhost:3000/posts/2" 수정할 id 값을 주소 마지막에 입력합니다.
- 데이터를 수정하기 위해서 method에 PUT를 입력합니다.

 

id 값이 2번인 데이터가 수정된 콘솔창

실제로 코드 실행 시 id값이 2인 title 값이 javaScript에서 fetch api로 변경되었고 author 값이 Mike에서 john으로 변경된 것을 확인할 수 있습니다.

 

 

데이터 삭제

function deleteData() {
      fetch("http://localhost:3000/posts/2", { // 삭제할 id 값을 입력합니다.
        method: "DELETE" // 삭제를 위해 method는 DELETE를 입력합니다.
      }).then(response => response.json()).then(json => console.log(json))
    }

위 코드는 fetch api를 이용해 데이터를 삭제하는 코드 입니다.

 

- fetch("http://localhost:3000/posts/2" 삭제할 id 값을 주소 마지막에 입력합니다.
- 데이터를 삭제하기 위해서 method에 DELETE를 입력합니다.

 

id값이 2인 데이터가 삭제된 콘솔창

실제로 코드 실행 시 id값이 2인 데이터가 삭제된 모습을 확인할 수 있습니다.

 

 


여기까지 json-server를 설치하고 fetch api를 이용해 간단한 데이터 조작까지 알아보았습니다.
실제 서버 api 주소가 없더라도 가상 서버인 json-server를 이용해 미리 코드를 구현하고 실제 api 주소를 전달받으면 주소만 교체하여 일을 효율적으로 처리할 수 있게 됩니다.

여기까지 저의 글을 읽어주셔서 감사합니다.

 


 

다. 참고자료

1. https://www.youtube.com/watch?v=NMJiqIadnMc - 개발자의 품격, 프론트엔드 개발자가 꼭 알아야하는 가짜 서버(json-server) 사용하기
2. https://ko.javascript.info/fetch - javascript.info, fetch

 

 

 

 

 

 

 

본 글은 Array(배열) 객체에서 사용할 수 있는 내장 함수의 종류와 사용법에 대해서 알아보고자 합니다.

서버에서 데이터를 클라이언트로 전송 받고 이 데이터를 통해 화면에 구현하는 경우가 많이 발생 합니다.

array 객체 내장 함수들을 알고 있다면 데이터를 적절하게 가공하여 화면에 나타낼 수 있습니다.

 

- 목차 - 

가. Array(배열)의 의미

나. Array(배열) 객체 내장 함수

 1. toString() 함수

 2. join() 함수

 3. pop() 함수

 4. push() 함수

 5. shift() 함수

 6. unshift() 함수

 7. splice() 함수

 8. concat() 함수

 9. slice() 함수

 10. sort() 함수

 11. filter() 함수

 12. map() 함수

 13. reduce() 함수

다. 참고자료

 

 

 

 

가. Array(배열)의 의미

배열 객체의 내장함수를 알아보기 전에 배열의 의미와 특징을 간단하게 알아봅시다.

 

배열은 하나 이상의 데이터를 담을 수 있으며 해당 데이터는 어떤 데이터 타입도 할당할 수 있습니다.

const array = [12, "abc", true, false, [], {}, undefined, null, Symbol]

 

어떤 데이터 타입도 담을 수 있는 배열

 

 

배열의 각 요소에는 순서가 있어서 데이터에 접근하고 수정할 수 있습니다.

배열의 순서는 0부터 시작합니다. 위의 사진에서도 0번 째 요소는 12를 명시하고 있습니다.

 

const array = [12, "abc", true, false, [], {}, undefined, null, Symbol]
console.log(array[2]) // true

 

위의 코드와 같이 배열에서 요소를 가져올 때는 배열 변수명에 대괄호를 사용합니다.

그리고 대괄호 안에 가져올 요소의 순번을 작성합니다.

배열 요소의 순서는 0부터 시작하므로 array[2]인 경우 true가 콘솔에 표시됩니다.

 


 

나. Array(배열) 객체 내장 함수

내장 함수는 자바스크립트에서 기본적으로 제공하는 함수를 의미합니다.

그렇기에 내장 함수를 잘 알고 있다면 이미 있는 기능을 활용하여 코딩할 수 있게 됩니다.

아래 글 부터 배열에서 사용할 수 있는 내장 함수에 대해서 알아봅시다.

 

1. toString() 함수

- 배열의 각 요소를 하나의 문자열로 반환하며 이때 각 요소는 ,(콤마)로 구분됩니다.

 

// toString() 함수
const fruits = ["apple", "banana", "pear", "orange", "melon"]
console.log(fruits.toString()) // apple,banana,pear,orange,melon

 

console에 표시된 toString() 함수 결과

 

 

 

2. join() 함수

- 첫 번째 파라미터로 전달된 문자가 각 요소 사이의 구분자로 사용됩니다.

- toString() 함수의 경우 ,(콤마)만 구분자로 사용되지만 join() 함수는 어떤 문자든 사용할 수 있습니다.

 

// join() 함수
const fruits = ["apple", "banana", "pear", "orange", "melon"]
console.log(fruits.join(" & ")) // apple & banana & pear & orange & melon
console.log(fruits.join("")) // applebananapearorangemelon

 

console에 표시된 join() 함수 결과

 

* join() 함수의 활용법

let drinkList = [ // 서버에서 전달받은 데이터로 가정
      {name: "콜라", price: 1400},
      {name: "칠성사이다", price: 1300},
      {name: "아메리카노", price: 1200},
      {name: "바나나우유", price: 1500},
      {name: "초코우유", price: 1000}
    ]

    const trElem = [] // 빈 배열 생성
    drinkList.forEach(function (drink) { // forEach 함수로 배열 순회
      trElem.push("<tr>") 
      trElem.push("<td>" + drink.name + "</td>")
      trElem.push("<td>" + drink.price + "</td>")
      trElem.push("</tr>") // push() 함수로 빈 배열에 요소 추가
    })

    document.getElementById('tbody_elem').innerHTML = trElem.join("")
    // 배열 join()함수를 통해 html 요소에 삽입

서버에서 전달 받은 데이터를 html 요소에 삽입할 경우

복합 할당 연산자인 +=로 태그들을 빈 문자열에 넣어 html에 넣는 방법이 있습니다.

 

하지만 위에 코드에서 push() 함수로 빈 배열을 채운 다음

마지막에 join()함수로 배열을 하나의 문자열로 만들어 html에 넣는 방법도 있습니다.

 

join() 함수 활용법 코드 출력화면

 

 

 

 

3. pop() 함수

- 배열의 마지막 요소를 제거하고, 해당 마지막 요소를 반환합니다.

// 3. pop() 함수
const fruits = ["apple", "banana", "pear", "orange", "melon"]
console.log(fruits.pop()) // melon
console.log(fruits) // ['apple', 'banana', 'pear', 'orange']

위 배열의 마지막 요소가 melon 이므로 fruits.pop()의 반환 값은 melon이 됩니다.

pop()함수로 인해 배열은 마지막 요소인 melon이 제거된 배열이 됩니다.

 

console에 표시된 pop() 함수 결과

 

 

 

4. push() 함수

- 배열 끝에 새로운 요소를 추가하고, 배열의 길이를 반환합니다.

// 4. push() 함수
const fruits = ["apple", "banana", "pear", "orange", "melon"]
fruits.push("grape") // grape 추가
console.log(fruits) // ['apple', 'banana', 'pear', 'orange', 'melon', 'grape']

위 배열에서 push() 함수로 grape를 추가하여 총 요소가 6개인 배열이 됩니다.

 

console에 표시된 push() 함수 결과

 

 

5. shift() 함수

- 배열의 첫 번째 요소를 제거하고 그 요소를 반환합니다.

 

// 5. shift() 함수
const fruits = ["apple", "banana", "pear", "orange", "melon"]
console.log(fruits.shift()) // apple 반환
console.log(fruits) // ['banana', 'pear', 'orange', 'melon']

 

shift() 함수를 통해 fruits 배열의 첫 번째 요소인 "apple"이 제거되었고 콘솔에는 apple이 반환되었습니다.

따라서 fruits 배열에는 "apple"요소를 제외한 나머지 요소들로 구성됩니다.

 

console에 표시된 shift() 함수 결과

 

 

6. unshift() 함수

- 배열 맨 앞에 요소를 추가하고, 배열의 길이를 반환합니다.

 

// 6. unshift() 함수
const fruits = ["apple", "banana", "pear", "orange", "melon"]
console.log(fruits.unshift("grape")) // 배열의 길이인 6 반환
console.log(fruits) // ['grape', 'apple', 'banana', 'pear', 'orange', 'melon']

 

unshift()로 fruits 배열 맨 앞에 "grape"가 추가되었고, 반환값은 배열의 길이인 6이 반환됩니다.

fruits 배열은 "grape"가 추가되어 총 길이가 6인 배열이 됩니다.

 

console에 표시된 unshift() 함수 결과

 

 

 

7. splice() 함수

- 배열 특정 위치에 새로운 요소를 추가하거나 요소를 제거하면서 추가할 수도 있습니다. 제거된 요소가 반환됩니다.

- 첫 번째 파라미터 : 새로운 요소를 추가할 인덱스 번호

- 두 번째 파라미터 : 요소를 추가하기전에 삭제할 요소 수 (없으면 첫 번째 파라미터 인덱스 번호부터 끝까지 삭제됩니다)

- 나머지 파라미터 : 추가할 요소 (없으면 요소를 추가하지 않습니다)

* 두번째 파라미터부터 필수값이 아닙니다.

 

// 7. splice() 함수
const fruits = ["apple", "banana", "pear", "orange", "melon"]
console.log(fruits.splice(2, 2, "grape")) // pear, orange 반환
console.log(fruits) // ['apple', 'banana', 'grape', 'melon']

 

splice() 함수로 첫 번째 파라미터 2는 인덱스 번호로 pear요소이며, 두 번째 파라미터 2는 2개를 삭제하므로 pear, orange 요소를 삭제하게 됩니다. 마지막 파라미터인 "grape"는 첫 번째 파라미터인 2번 인덱스 위치에 추가됩니다.

 

console에 표시된 splice() 함수 결과

 

 

 

 

8. concat()

- 2개 이상의 배열을 하나의 배열로 결합합니다.

- ,(콤마)를 통해 배열을 계속 결합이 가능합니다. (ex. arr01.concat(arr02, arr03, ...))

 

// 8. concat() 함수
const fruits01 = ["apple", "banana"]
const fruits02 = ["pear", "orange", "melon"]
console.log(fruits01.concat(fruits02)) // ['apple', 'banana', 'pear', 'orange', 'melon']

 

fruits01 배열과 fruits02 배열을 결합하여 총 5개 요소를 가진 배열이 반환됩니다.

 

console에 표시된 concat() 함수 결과

 

 

* Spread Operator로도 배열 결합이 가능합니다.

const fruits01 = ["apple", "banana"]
const fruits02 = ["pear", "orange", "melon"]
console.log([...fruits01, ...fruits02]) // ['apple', 'banana', 'pear', 'orange', 'melon']

배열 변수명 앞에 ... 을 붙이면 개별요소로 분해합니다. 이것을 배열로 만들면 concat() 함수와 동일한 값이 반환됩니다.

 

 

 

9. slice() 함수

- 배열의 요소를 잘라내서 새 배열을 반환합니다.

- 첫 번째 파라미터 : 시작 인덱스

- 두 번째 파라미터 : 종료 인덱스 (없으면 시작인덱스부터 끝까지 잘라냅니다)

 

// 9. slice() 함수
const fruits = ["apple", "banana", "pear", "orange", "melon"]
console.log(fruits.slice(2, 4)) // pear, orange 반환
console.log(fruits) // ['apple', 'banana', 'pear', 'orange', 'melon']

 

slice() 함수의 시작인덱스가 2이므로 pear부터, 종료인덱스 4의 경우 자신은 포함하지 않고 바로 앞까지 범위가 지정되어 orange요소까지 잘라냅니다. 따라서 pear, orange를 반환합니다.

원본배열은 수정되지 않습니다.

 

console에 표시된 slice() 함수 결과

 

 

 

10. sort() 함수

- 배열 요소를 정렬합니다.

- 원본 배열이 정렬되는 것을 인식하고 있어야 합니다.

 

// 10. sort() 함수
const fruits = ["apple", "banana", "pear", "orange", "melon"]
console.log(fruits.sort()) // ['apple', 'banana', 'melon', 'orange', 'pear']

 

배열에 sort() 함수를 사용하면 오름차순으로 정렬 됩니다.

 

 

 

 

* 숫자 정렬

const num = [9, 2, 100, 250, 50, 80, 31]
console.log(num.sort()) // [100, 2, 250, 31, 50, 80, 9]

위의 숫자 배열을 sort()함수로 정렬 시 문자열로 비교하게 되어 원하는 정렬을 얻지 못합니다.

 

 

 

 

* 정렬순서에 대한 함수를 sort()함수의 파라미터로 정의하면 원하는 정렬을 반환할 수 있습니다.

 

const num = [9, 2, 100, 250, 50, 80, 31]

    num.sort(function (a, b) {
      if (a > b) return 1;
      if (a < b) return -1;
      if (a === 0) return 0;
      
      // a = 9, b = 2 / a > b이므로 9와 2는 교체
      // [2, 9, 100, 250, 50, 80, 31]
      
      // a = 9, b = 100 / a < b이므로 그대로 유지
      // [2, 9, 100, 250, 50, 80, 31]
      
      // a = 100, b = 250 / a < b이므로 그대로 유지
      .
      .
      .
      // [2, 9, 31, 50, 80, 100, 250] 최종적으로 오름차순 배열 형태로 정렬
    })
    console.log(num) // [2, 9, 31, 50, 80, 100, 250]

 

파라미터 a와 b에 배열 요소가 순차적으로 들어가고 그 둘을 비교합니다.

a와 b를 비교해서 리턴값이 양수이면 a와 b의 자리를 교체합니다.

a와 b를 비교해서 리턴값이 음수이면 a와 b의 자리를 교체하지 않습니다.

더 이상 a와 b의 자리가 교체되지 않을 때까지 진행되면 원하는 정렬을 얻게 됩니다.

 

 

 

따라서 양수인지 음수인지를 확인하므로 숫자 정렬의 경우 아래와 같이 축약할 수 있습니다.

 

const num = [9, 2, 100, 250, 50, 80, 31]
num.sort(function (a, b) { return a - b })
console.log(num) // [2, 9, 31, 50, 80, 100, 250]

 

 

내림차순의 경우 아래와 같이 작성할 수 있습니다.

 

const num = [9, 2, 100, 250, 50, 80, 31]
num.sort(function (a, b) { return b - a })
console.log(num) // [250, 100, 80, 50, 31, 9, 2]

 

오름차순을 a - b로 비교했다면 내림차순은 b - a로 하여 정렬할 수 있습니다.

 

 

 

 

* 객체 정렬 & 문자 정렬

let drinkList = [
      {name: "콜라", price: 1400},
      {name: "칠성사이다", price: 1300},
      {name: "아메리카노", price: 1200},
      {name: "바나나우유", price: 1500},
      {name: "초코우유", price: 1000}
    ]

    drinkList.sort(function (a, b) {
      if (a.name > b.name) return 1; // 양수 : 자리교체
      if (a.name < b.name) return -1; // 음수 : 자레교체 안함
      if (a.name === b.name) return 0; // 0 : 다음 순서 진행
    })

    console.log(drinkList)

 

객체에 접근하여 문자를 정렬할 경우 숫자 정렬할 때의 a - b로 정렬되지 않고 if 문을 사용하여 정렬할 수 있습니다.

console에 표시된 sort() 함수 결과

 

 

 

11. filter() 함수

- 배열에서 특정 조건에 맞는 요소만 모아 새 배열로 반환됩니다.

- filter() 함수의 파라미터로 함수가 사용되며 그 함수의 파라미터에는 배열의 요소가 포함됩니다.

- 원본 배열은 변경되지 않습니다.

 

 const drinkList = [
      { name: "콜라", price: 1400 },
      { name: "칠성사이다", price: 1300 },
      { name: "아메리카노", price: 1200 },
      { name: "바나나우유", price: 1500 },
      { name: "초코우유", price: 1000 }
    ]

    const drinkListFilter = drinkList.filter(function (drink) {
      return drink.price > 1200; // 조건이 true 인 요소로 새로운 배열이 반환됩니다.
    })
    console.log(drinkListFilter)

 

filter() 함수는 for 반복문을 사용한 것처럼 배열을 순회하고 함수 안에 조건이 true인 요소만 새로운 배열로 반환됩니다.

위 코드에서는 price가 1200 이상인 요소들만 true가 되므로 name 값이 콜라, 칠성사이다, 바나나우유인 요소로 새로운 배열이 반환됩니다.

console에 표시된 filter() 함수 결과

 

 

 

 

12. map() 함수

- 배열이 가지고 있는 요소가 객체인 경우, 객체가 가지고 있는 key - value 쌍을 새로운 객체 형태로 변경하여 새 배열을 반환합니다.

- map() 함수의 파라미터로 함수가 사용되며 그 함수의 파라미터에는 배열의 요소가 포함됩니다.

- 배열 안의 객체들을 재정의 합니다.

- 서버에서 필요한 정보만 재정의하여 클라이언트로 데이터를 전달하는 경우도 있습니다.

- 원본 배열은 변경되지 않습니다.

 

const products = [
      {size: "big", name: "radio", price: 30000},
      {size: "small", name: "computer", price: 40000},
      {size: "medium", name: "phone", price: 50000}
    ]
    const productMap = products.map(function (prod) {
      return { // 배열 요소의 객체를 재정의 합니다.
        sizeProduct: prod.size + " " + prod.name,
        price: prod.price
      }
    })
    console.log(productMap)

 

위 코드에서 객체의 키 명인 size와 name이 있었으나 map() 함수를 통해 sizeProduct 라는 키로 합치게 되어 새 객체로 재정의 됩니다.

 

console에 표시된 map() 함수 결과

 

 

 

 

13. reduce() 함수

- reduce() 함수의 파라미터로 함수가 사용되며 그 함수의 파라미터는 아래와 같습니다.

- 첫 번째 파라미터 : accumulator - 누적값

- 두 번째 파라미터 : currentValue - 현재 배열 요소

- 세 번째 파라미터 : currentIndex - 현재 배열 인덱스 번호 (생략 가능)

- 네 번째 파라미터 : array - 전체 배열 (생략 가능)

- 원본배열은 변경되지 않습니다.

 

// 13 reduce()
const num = [9, 2, 100, 250, 50, 80, 31]
let sum = num.reduce(function (accumulator, currentValue, currentIndex, array) {
     return accumulator + currentValue;
}, 0) // 0은 초기화 값, accumulator(누적값) 초기값 : 0
console.log(sum) // 522

 

reduce함수는 배열의 개별 요소를 currentValue에 넣고 accumulator에 값이 누적됩니다.

 

 

 

 

아래는 reduce()함수 작동 순서를 알아보기 위한 코드 입니다.

 

// 13 reduce()
    const num = [9, 2, 100, 250, 50, 80, 31]
    let sum = num.reduce(function (accumulator, currentValue, currentIndex, array) {
      console.log(currentIndex + 1 + "번째 진행")
      console.log(accumulator + " - 누적 값")
      console.log(currentValue + " - 현재 값")
      console.log(accumulator + " + " + currentValue + " - 누적값 + 현재 값")
      console.log("")
      return accumulator + currentValue;
    }, 0) // 초기화 값 0
    console.log("총 누적 값 : " + sum) // 522

 

1번째 진행

reduce() 함수의 두 번째 파라미터를 0으로 할 시 처음 accumulator 초기값은 0입니다.

currentValue에는 num의 첫 번째 요소인 9가 들어가고 누적 값과 더해집니다.

 

2번째 진행

num의 9와 합하여 누적 값은 9입니다.

currentValue에는 num의 두 번째 요소인 2가 들어가고 누적 값인 9와 더해집니다.

 

이런 식으로 배열 요소의 갯수 만큼 반복되고 총 합계인 522가 반환됩니다.

 

console에 표시된 reduce() 함수 결과

 

 

* 조건식에서 사용되는 reduce() 함수

const drinkList = [
      { name: "콜라", price: 1400 },
      { name: "칠성사이다", price: 1300 },
      { name: "아메리카노", price: 1200 },
      { name: "바나나우유", price: 1500 },
      { name: "초코우유", price: 1000 }
    ]

    const price = drinkList.reduce(function (acc, cur) {
      if (cur.price > 1200) { // price가 1200 초과
        acc.push(cur) // 누적값의 초기값을 배열로 설정하여 누적값 배열에 push()함수로 현재 요소 넣기
      }
      return acc; // 누적값 반환
    }, []) // 초기값 배열로 설정
    
    console.log(price)

위 코드는 숫자 누적 합이 아닌 if 조건문을 이용하여 조건에 맞는 값만 반환하는 코드 입니다.

누적값의 초기값을 배열로 설정하고 현재 요소(cur)를 push()함수로 넣어 누적값을 반환합니다.

price값이 1200 초과인 배열요소, 즉 name 값이 콜라, 칠성사이다, 바나나우유인 요소가 반환됩니다.

 

console에 표시된 reduce() 함수 결과

 


 

여기까지 array(배열) 객체 내장 함수들을 알아보았습니다.

내장 함수들을 학습해 놓는다면 배열을 합치거나 요소를 정렬하는 등의 함수를 별도로 만들지 않더라도 구현할 수 있게 됩니다.

 

여기까지 저의 글을 읽어주셔서 감사합니다.

 

 


 

 

 

다. 참고자료

1. https://www.youtube.com/c/개발자의품격 / 부트캠프 中 - array 객체 내장 함수

2. https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/toString - MDN, toString()

3. https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/filter - MDN, filter()

4. https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce - MDN, reduce()

 

본 글은 자바스크립트 반복문의 종류와 그에 대한 사용법에 대해서 알아보고자 합니다.

 

- 목차 - 

가. 반복문 의미와 사용하는 이유

나. 반복문의 종류와 사용법

 1. for-loop 문

 2. for in 문

 3. for of 문

 4. forEach 문

 5. while 문

 6. do while 문

다. 참고자료

 


 

가. 반복문 의미와 사용하는 이유

특정 코드 블록을 정해진 횟수만큼 반복해서 실행되게 만드는 것을 반복문이라고 합니다.

즉, 특정 기능이나 명령을 반복해서 수행하기 위함입니다.

아래 예제에서 반복문을 사용하는 이유를 쉽게 알아보고자 합니다.

 

웹 화면에 1에서 100까지의 숫자를 출력하는 경우를 가정해봅시다.

반복문을 사용하지 않는다면 아래와 같이 표현할 수 있습니다.

 

document.write('1');
document.write('2');
document.write('3');
        .
        .
        .
document.write('98');
document.write('99');
document.write('100');

 

시간을 투자하여 1에서 100까지 숫자를 위와 같이 표현할 수도 있습니다.

하지만 나중에 1,000까지, 10,000까지 숫자로 표현해야 한다면 많이 힘들어질 수 있습니다.

이런 경우 중복되는 코드를 줄이고 반복해서 실행할 수 있도록 하는 것반복문입니다.

 

다음 글부터 자바스크립트 반복문의 종류와 사용법에 대해서 알아봅시다.

 


나. 반복문의 종류와 사용법

반복문의 종류에는 for-loop, for-in, for-of, forEach, while, do while이 있습니다.

왼쪽부터 차례대로 알아봅시다.

 

 

 

1. for-loop 문

for-loop 문은 초기식, 조건식, 증감식이 포함되어 있는 반복문 입니다.

for-loop 문은 객체에서 사용할 수 없으며 배열에서 사용 가능합니다.

아래 코드는 for loop문의 기본 형식입니다.

 

// for 문의 형식
for(statement1 ; statement2 ; statement3){
     실행될 코드
}

 

- statement1 : for-loop문 실행할 때 무조건 딱 한번 실행
- statement2 : 코드 블록을 실행시킬 조건절
- statement3 : 코드블록을 실행한 후 실행되는 코드

 

 

 

아래 for-loop문을 이용한 예제 코드를 보면서 이해하고자 합니다.

 

// 1에서 100까지 숫자를 출력
for (let i = 1; i <= 100; i++) {
      document.write(i);
}

 

1) statement1에 해당하는 "let i = 1" 은 한 번 실행됩니다. (변수 i에 숫자 1이 할당됩니다)

2) statement2에 해당하는 조건인 "i <= 100" 은 i 가 숫자 100보다 작거나 같으면 아래 코드가 실행됩니다.

3) statement3에는 i++가 있습니다. 이것은 for-loop문 안 코드가 실행된 후 i가 1씩 증가됨을 의미합니다.

 

 

아래는 위의 코드가 실행되는 순서를 표현한 코드입니다.

 

for (let i = 1; i <= 100; i++) {
       i = 1,  1이 100보다 작으므로 조건에 부합
       document.write(1) 실행
       i++되어 i는 2가 됨

       i = 2, 2는 100보다 작으므로 조건에 부합
       document.write(2) 실행
       i++되어 i는 3이 됨
      .
      .
      .
       i = 100, 100은 100과 같으므로 조건에 부합
       document.write(100) 실행
       i++되어 i는 101이 됨.

       i = 101, 101은 100보다 크므로 조건에 부합하지 않음
       for문이 종료되고 코드블록을 빠져나감
    }

 

위와 같이 조건에 부합하면 계속 반복실행되며 조건에 부합되지 않으면 for-loop문이 종료됩니다.

 

 

 

* for-loop 문 - break

break는 for-loop문을 사용하면서 원하는 특정 시점에 for 블록 전체를 실행하지 않고 빠져나올 수 있는 용도로 사용합니다.

 

for (let i = 0; i < 10; i++) {
      if (i === 3) {
        break; // break를 만나면 for문 종료
      }
      console.log(i); // 0, 1, 2 콘솔에 출력
    }

 

위의 for-loop문을 break 없이 실행한다면 0에서 9까지 콘솔에 출력되어야 하지만 

if 조건문을 사용, i 가 3일 때 break를 적용하므로 콘솔에는 0, 1, 2만 출력되고 for-loop문을 빠져나옵니다.

 

 

* for-loop 문 - continue

continue는 for-loop문을 사용하면서 continue를 만나면 아래 실행 코드를 실행하지 않고 statement3으로 이동됩니다.

 

for (let i = 0; i < 10; i++) {
      if (i === 3) {
        continue; // continue를 만나면 아래 코드를 실행하지 않고 i++로 이동 후 진행
      }
      console.log(i); // 0, 1, 2, 4, 5, 6, 7, 8, 9 콘솔에 출력 (3 생략)
    }

 

continue가 없다면 0~9까지 콘솔에 출력되지만 i가 3일 때 continue가 실행되므로

i가 3일 때의 continue 아래 코드가 실행되지 않고 statement3인 i++로 진행됩니다.

따라서 콘솔에는 0, 1, 2, 4, 5, 6, 7, 8, 9가 출력됩니다. (3이 생략됩니다)

 

 

 

 

2. for in 문

배열에서 요소만큼 반복하고 인덱스 번호를 출력합니다.

for-loop문은 객체에서 사용할 수 없지만 for in은 객체에서 사용할 수 있습니다.

 

 

아래는 for in 문의 기본 형태입니다.

 

// for in 기본 형태
for (선언자 변수명 in 객체) {
      실행 코드
    }

 

 

 

아래 for-loop문과 비교하면서 알아봅시다.

 

const fruit = ["orange", "apple", "banana", "grape"];

    // for 문
    for (let i = 0; i < fruit.length; i++) {
      console.log(fruit[i]);
    }

    // for in 문
    for (const i in fruit) {
      console.log(fruit[i]);
    }

 

위의 for와 for in 은 똑같은 결과를 가집니다.

for 같은 경우 statement를 설정해야 하지만 for in은 따로 statement를 설정하지 않더라도 모든 배열 요소를 순회합니다.

for in의 변수명에는 배열의 인덱스 번호가 출력됩니다. (위의 예제에서는 배열의 인덱스 번호가 0, 1, 2, 3이 됩니다)

 

 

* object에서 사용되는 for in

 

let person = {
      name: "김미영",
      age: 28,
      tel: "010-1234-5678",
      city: "daegu"
    };

    for (const key in person) { // key에는 person 객체의 키 명이 입력됩니다.
      console.log(key, person[key])
    }

 

객체에서 사용되는 for in 은 변수명에 객체의 키 명이 입력되어 위 예제 key에는 name, age, tel, city가 담겨있습니다.

객체의 키 값을 출력하기 위해서는 객체[키]를 이용하며 객체의 모든 키 값을 출력할 수 있습니다.

위의 예제에선 person[key]로 가져올 수 있습니다. (김미영, 28, 010-1234-5678, daegu가 출력됨)

 

 

- for in을 사용하는 경우

(1) 객체의 키, 값을 모두 출력해야 하는 상황

(2) 객체의 키 명을 알 수 없을 때 for in을 사용 가능

 

 

 

 

3. for of 문

배열의 요소만큼 반복하고 요소 값을 출력할 수 있습니다.

 

const fruit = ["orange", "apple", "banana", "grape"];
    for (const fruitsName of fruit) { // 배열을 순회하면서 fruitsName에 요소값을 넣어준다.
      console.log(fruitsName) // orange, apple, banana, grape
    }

 

for in은 변수명에 인덱스 번호가 담긴다면 for of는 배열 각각의 요소 값이 입력됩니다.

위의 예제 콘솔에 변수명 fruitsName을 출력하면 orange, apple, banana, grape가 출력됩니다.

 

 

* 문자열에 사용되는 for of 문

문자열을 문자 하나씩 출력할 수 있습니다.

 

const str = "js is the best language";
    for (const string of str) {
      console.log(string)
    }

 

위의 코드를 실행 시 아래와 같이 출력됩니다.

 

문자열을 for of로 출력한 모습

 

 

 

4. forEach 문

forEach문은 배열의 내장함수로 사용됩니다.

forEach의 파라미터는 함수를 전달 받습니다.

*파라미터(매개변수)란 함수를 호출할 때 전달받은 인수를 함수 내부로 전달하기 위한 변수를 의미합니다.

배열을 순회하면서 배열의 요소값, 인덱스번호를 출력할 수 있습니다.

 

const fruit = ["orange", "apple", "banana", "grape"]; // fruit - 배열 객체
  fruit.forEach(function (item, index) { // array 내장함수, 첫번째 파마리터 : 요소값, 두번째파라미터 : 인덱스번호
    console.log(item) // orange, apple, banana, grape 출력
    console.log(index) // 0, 1, 2, 3 출력
   })

 

위 예제의 첫번째 파라미터 item에는 배열의 요소 값인 orange, apple, banana, grape를 전달받고

두번째 파라미터 index에는 인덱스 번호인  0, 1, 2, 3을 전달 받습니다.

 

 

 

 

5. while 문

while문은 조건식을 만족하는 동안 코드 블록을 실행합니다.

조건식이 만족하는 동안 실행되므로 while문을 종료할 수 있는 코드가 반드시 존재해야 합니다.

 

let i = 1;
    while (i < 5) { // 변수 i가 5미만일 때만 while문 실행
      console.log(i)
      i++; // 반복할 때마다 변수 i를 1씩 증가, i가 5가되면 while문 종료
      
      // i = 1, i < 5 코드 블록 실행
      // i = 2, i < 5 코드 블록 실행
      // i = 3, i < 5 코드 블록 실행
      // i = 4, i < 5 코드 블록 실행
      // i = 5, i < 5 조건식에 부합하지 않으므로 while 문 종료
    }

 

조건식에 부합하는 동안 반복하다가 조건에 부합하지 않게 되면 while문은 종료됩니다.

위의 예제에서는 i 가 5 미만일 때까지 반복하다가 5가 되면 조건에 부합하지 않으므로 종료됩니다.

 

* while문이 사용되는 경우

얼마나 반복해야 될지 모를 때 사용합니다.

 

// 동전교환기
    let inputCoin = 6000; // 넣은 금액
    let coinUnit = 500; // 교환할 동전
    let coinCount = 0; // 교환 동전의 갯수, 초기값은 0

    while (inputCoin >= 0) { // 넣은 금액이 0보다 클 경우 반복 (조건식)
      inputCoin = inputCoin - coinUnit; // 넣은 금액 - 교환할 동전, 넣은 금액이 0보다 작을 때까지 반복
      coinCount++; // 교환 동전 갯수 1씩 증가
    }

 

위의 예제는 동전교환기 코드입니다.

동전 교환기의 경우 사용자가 돈을 얼마나 교환할지 정해져 있지 않습니다.

따라서 얼마나 반복해야 할지 알 수 없으므로 while문을 통해 반복 횟수를 명시하지 않더라도 반복문을 사용할 수 있게 됩니다.

 

 

 

6. do while 문

do while문은 무조건 1번 코드 블록이 실행되고, 그 다음 조건식을 체크합니다.

 

let i = 1;
    do {
      console.log(i) // 무조건 1번 실행되므로 console에 1이 출력된다.
      i++;
    } while (i > 5)

 

위의 예제에서 조건식인 i 가 5보다 커야 조건에 부합하지만 do while 문은 코드블록이 무조건 1번 실행되므로 콘솔에 1일 출력되고 do while문이 종료됩니다.

 


 

여기까지 자바스크립트 반복문에 대해서 알아보았습니다.

배열 및 객체에서 반복문을 통해  값 또는 인덱스 번호를 가져올 수 있으며 반복되는 코드를 줄일 수 있는 장점이 있습니다.

반복문에는 6가지가 있으며 각각의 쓰임새가 조금씩 다르므로 필요에 맞게 사용하시면 됩니다.

 

저의 글을 읽어주셔서 감사합니다.

 


 

 

다. 참고자료

 

1. https://www.youtube.com/c/개발자의품격 / 부트캠프 中 - 반복문

2. http://www.tcpschool.com/javascript/js_control_loop - TCH SCHOOL - 반복문

3. http://www.tcpschool.com/javascript/js_function_parameterArgument - TCP SCHOOL - 매개변수와 인수

+ Recent posts