When an HTML document has been switched to designMode, its document object exposes an execCommand method to run commands that manipulate the current editable region, such as form inputs or contentEditable elements.
즉, HTML document가 디자인 모드로 바뀌면서 exeCommand를 사용하면, form의 input과 같이 편집이 가능한 element를 조작할 수 있다는 것이다. 그래서 element의 내용을 클립보드로 복사할 수 있게 되었다고 이해하면 좋을 것 같다.
따라서, 어떤 element의 innertext를 복사하고자 한다면, 그 element가 editable한 input이나 textarea여야 한다.
만약, 해당 element가 input이나 textarea가 아니라면, 아래의 코드처럼 textarea를 만들었다가 지우는 방식을 사용하면 된다.
*하루동안 새롭게 알게된 부분, 에러를 해결한 방법 등을 작성하는 개발일기입니다. 다른 사람에게도 설명해줄 수 있도록 제 머릿속에 넣기 위해 정리를 시작하게 되었습니다.
React 버튼 클릭 효과 유지하기
페이지 버튼이든 카테고리 버튼이든, 아래처럼 버튼을 클릭했을 때 style을 유지하고 싶은 경우가 있다.
클릭 효과를 유지하기 위한 방법은 여러가지가 있겠지만, 나는 클릭 이벤트 리스너를 붙여 classList를 추가해주고 지우는 방식을 택했다.
// 각 페이지 버튼마다 'pages'라는 클래스를 가지고 있다. const pages = document.getElementsByClassName('pages');
const clickHandler = (e) => {
// 처음 로딩되었을 때는 1번 페이지에 선택되어 있어야 하기 때문에, // 1번 페이지 버튼은 'first-page' 클래스를 가지고 있다. // 만약, 클릭된 클래스 리스트에 first-page가 없다면 첫번째 페이지는 // 계속 선택되어 있을 필요가 없기 때문에 remove 해준다. if (!e.target.classList.contains("first-page")) {
pages[1].classList.remove("first-page");
}
// 모든 페이지 버튼에서 활성 클래스인 'active-page'를 지워 초기화 해준다. for (var i = 0; i < pages.length; i++) {
pages[i].classList.remove("active-page");
}
// 현재 클릭된 버튼에만 활성 클래스인 'active-page'를 붙여준다.
e.target.classList.add("active-page");
}
// 모든 pages 클래스를 가진 요소에 클릭 이벤트 리스너를 붙여주었다. // 그래서 클릭 이벤트가 발생하면, clickHandler 함수가 실행된다. const init= () => {
for (let i = 0; i < pages.length; i++) {
pages[i].addEventListener("click", clickHandler);
}
}
init();
addEventListener - 특정 이벤트가 발생했을 때 특정 함수를 실행할 수 있게 해주는 기능. 여기서는 click 이벤트가 발생했을 때 clickHandler 함수를 실행해준다.
document.getElementsByClassName(className) - HTML에서 있는 요소 중, 주어진 클래스명을 가진 모든 요소들을 배열에 담아 리턴해준다.
e.target - 이벤트 객체의 타깃, 예를 들어, 2번 페이지를 눌렀다면 <li class="pages active-page" style="user-select: auto;">2</li>가 리턴된다.
*하루동안 새롭게 알게된 부분, 에러를 해결한 방법 등을 작성하는 개발일기입니다. 다른 사람에게도 설명해줄 수 있도록 제 머릿속에 넣기 위해 정리를 시작하게 되었습니다.
React에서 map 사용 시 key를 설정해줘야 하는 이유는
아래와 같이 컴포넌트를 반복하여 렌더링해줘야 하는 경우, map을 사용한다.
그런데 이 때, 배열 내부의 엘리먼트에 key 값을 지정해주지 않으면, 'Each child in a list should have a unique key prop.' 경고문이 뜨게 된다.
리액트 공식문서에서는, key는 어떤 아이템이 변화되거나 추가 또는 삭제되었는지를 알아차리기 위해 필요하다고 한다.
리액트는 state에서 변경된 부분만 찾아 리렌더링해주는데, 이때 가상 dom과 비교하여 바뀐 부분만 리렌더링해준다. 만약, 배열에 요소가 추가되었다면배열 전체가 변경된 것이라고 생각하고 전체를 리렌더링 하게 된다. 마지막 요소만 변경되었으니, 전체를 리렌더링할 필요는 없는데도 말이다.
그래서 배열 내부 엘리먼트에 key를 지정해줘야 한다. key 값을 지정해주면 리액트는 배열에 추가된 요소에 대해서만 리렌더링한다. 즉, key는 배열의 어떤 원소에 변동이 있었는지 알아내고자 할 때 사용되는 것이다. 따라서, 데이터가 가진 고유의 값을 key로 설정해야 한다.
key 값으로 index를 설정하는 경우도 있는데, 어떤 경우에는 key 설정의 장점을 하나도 살리지 못하고 배열 전체가 리렌더링 될 수 있다. 배열에 첫 번째 위치에 새로운 element를 넣게 되면, 기존 배열의 index가 하나씩 밀리게 된다. 그럼 결국, 배열 전체가 변경되었다고 생각하기 때문에 배열 전체가 리렌더링 되는 것이다.
이유를 찾아보니, '라우터가 아닌' 컴포넌트에서 history(나 location, match)와 같은라우터에서 사용하는 객체를 쓰려면 withRouter라는 HOC을 사용해야 한다. 'react-router-dom'에서 'withRouter'를 import하고, export 하는 컴포넌트를 withRouter로 감싸주면 된다. HOC은 어제도 배웠지만, 컴포넌트를 인자로 받아서 새로운 컴포넌트를 리턴하는 함수이다.
*하루동안 새롭게 알게된 부분, 에러를 해결한 방법 등을 작성하는 개발일기입니다. 다른 사람에게도 설명해줄 수 있도록 제 머릿속에 넣기 위해 정리를 시작하게 되었습니다.
react redux axios post
회원가입 폼 제출 시, 해당 user가 넘긴 정보(nickname, phone, role)가 dispatch되고 axios post를 하는 코드를 작성했다.
여기서 잠깐 redux 개념을 정리하고 가자면,,,
redux는 데이터의 중앙 저장소이다. 각 컴포넌트에서 필요로 할때 꺼내 쓸 수 있는 장소라고 생각하면 된다.
redux에서 data를 바꾸기 위해서는 action이 필요하고, action을 dispatch하면 중앙 저장소의 data가 변경된다. 그런데 action을 dispatch한다고 data가 자동으로 바뀌는 것은 아니고, reducer에서 어떻게 바꿀지를 구현해주어야 한다.
회원가입 폼을 제출하면,registerUser 액션을 dispatch해중앙저장소에 user data를 추가해준다.
삽입 정렬은 array의 두번째 인덱스에서 시작해서 마지막 인덱스가 될때까지, 앞의 요소들과 비교해 작으면 삽입하는 정렬 방식이다.
functionsolution(arr){
const answer = arr;
for(let i = 1; i < arr.length; i++){
// tmp는 비교대상이 될 임시적 변수 const tmp = arr[i];
// j는 tmp의 앞 인덱스let j = i - 1for(; j >= 0; j--){
if(tmp < arr[j]){
arr[j+1] = arr[j];
}
else{
// tmp가 더 크면 더 이상 비교할 필요 없다. 앞의 요소들은 이미 정렬된 상태이므로break;
}
}
// j가 -1이 된 경우, 즉 모든 요소가 tmp보다 컸다면 arr[0]에 tmp에 들어가야 하고/*
중간에 break된 경우,
예를 들면 j가 2일때 break 되었다면,
arr[2]보다는 tmp가 크고, arr[4]보다는 tmp가 작다는 뜻이므로
j가 3인 자리에 tmp가 들어가야 한다.
*/// 즉, 둘 중 어떤 경우라도 항상 인덱스가 (j + 1)인 자리에 tmp가 들어가야 한다.
arr[j+1] = tmp;
}
return answer;
}
let arr = [11, 7, 5, 6, 10, 9];
console.log(solution(arr));
JavaScript Array.slice()
slice()는 배열에서 특정 인덱스부터 특정 인덱스까지의 복사가 필요할 때 주로 쓰인다. 즉, 원본 배열은 건드리지 않는 메소드이다.
arr.slice(begin, end) 형태로 쓰이며, 'begin'번째 인덱스부터 'end - 1'번째 인덱스까지 복사된다. 즉, end 인덱스는 미포함이다.
const animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];
console.log(animals.slice(2, 4));
// 결과: ["camel", "duck"]// 2번째 인덱스부터 (4-1)번째 인덱스까지 복사, 즉, 4번째 인덱스는 미포함// 배열 전체를 복사하고 싶을 때console.log(animals.slice());
console.log(animals.slice(2));
// 결과: ["camel", "duck", "elephant"]// 2번째 인덱스부터 끝 인덱스까지 복사됨
*하루동안 새롭게 알게된 부분, 에러를 해결한 방법 등을 작성하는 개발일기입니다. 다른 사람에게도 설명해줄 수 있도록 제 머릿속에 넣기 위해 정리를 시작하게 되었습니다.
React에서 Form을 사용해서 구현할 때 다양한 input을 받아야 하는 경우, 관리할 state가 많아지면서 코드가 쉽게 복잡해질 수 있다.
또, React에서는 Form 양식을 제출할 때, 성가신 것들이 몇 가지 있다.
Form 상태에서 값 가져오기
유효성 검사 및 오류 메세지
Form submit 핸들링
Formik라는 라이브러리를 사용하면 위의 세 가지 요소를 한 곳에 배치할 수 있어 편리하다고 한다. Formik는 Provider를 통해 상태와 메소드를 ui에 공유한다. 이에 따라 form 내에 있는 모든 컴포넌트들이 동일한 상태를 공유할 수 있게 된다.
위와 같은 장점들이 있어, 이번 프로젝트에서 Formik를 사용해볼까 했었는데, 내가 하는 프로젝트는 '시각 장애인들을 위한 커뮤니티'이다. 회원가입 시 번거로움을 덜어 드리고 싶어서, 전화번호만 입력을 받을 예정이라 그리 많은 input이 필요하지는 않아 그냥 React의 Form을 사용하기로 했다.
오늘은 만약 사용자가 중복된 전화번호를 입력했을 경우, 폼이 submit되지 않고 경고창이 뜨고, 전화번호 input이 reset되는 것을 구현했다.
이 때, 만약 중복된 전화번호를 입력했다면 '회원가입하기'를 눌렀을 때 아래와 같이 경고창이 뜨고
전화번호 input창이 reset되게 말이다. 그대로 둘 수도 있겠지만, 그러면 시각장애인들이 다시 input을 지워야 하니 번거로울 것 같아서 그냥 아예 reset을 했다.
중복확인을 하더라도 submit되어 data가 넘어가면 어쩌지 했는데, 만약 registered된 사용자라면, 'alert'문을 return하면 경고창만 띄워지고, data가 submit되지는 않았다.