들어가며
안녕하세요 초원입니다. 👩🏻💻
오늘은 발제 시간에 들었던 인상 깊었던 문장을 공유하며 시작해보려고 해요.
시간과 돈을 가장 덜 쓰게 만드는 것, 개발자가 해야 할 일. 충분히 할 수 있는 일!
저는 아직 클라이언트나 사용자와 소통할 일이 적어서
해당 문장을 팀에 적용해보려고 합니다.
나의 시간을 아끼고, 사수님의 시간을 아끼기 위해 저는 어떤 노력을 할 수 있을까요?
현재는 'pr 습관 잘 들이기'를 주제로 노력 중인데,
좋은 성과가 있다면 다음 글에 공유해보도록 하겠습니다 🙌🏻
2주차 참여 후기
긴 말 필요 없이 ..
본질은 자바스크립트다. 이 문장이 이번 주차 저의 후기입니다.
자바스크립트 메서드와 DOM API에 익숙하지 않아 너무 오래 걸렸어요 후
그래도 해냈다! 따봉
2주차 과제
본 게시글은 신입 개발자가 항해플러스 프론트엔드 2기 _리액트 파헤치기 과정을 거치며 고민한 내용을 담았습니다. 과제와 테스트 코드를 제공해주신 준일 코치님께 감사 인사 드립니다 🙇🏻♀️
createNumber
export function createNumber1(n) {
return n;
const numObj = new Number(n)
return numObj;
}
테스트 코드를 통과시키기 위해,
createNumber1 함수는 숫자가 아닌 객체를 반환해야 합니다. 하지만, 숫자 원시 값을 다룰 수 있어야 합니다.
실제 숫자 값을 다루려고 할 때 Number 객체는 예상치 못한 동작을 일으킬 수 있지만,
현재 테스트코드에서는 Number 객체가 기본적인 toString과 valueOf 메소드를 제공하기 때문에 문제가 없습니다.
createNumber2
export function createNumber2(n) {
return {
value: n.toString(),
toString: function() {
return this.value;
},
valueOf: function() {
return this.value;
}
};
}
테스트 코드를 통과시키기 위해,
createNumber2 함수는 객체를 반환해야 합니다. 하지만, 문자열 원시 값을 다룰 수 있어야 합니다.
객체 리터럴 방식을 사용하여 내부에 문자열 원시 값을 반환하는 value 속성을 추가했고,
createNumber2 객체의 value를 반환하는 toString과 valueOf 메서드를 정의합니다.
이때, 화살표 함수를 사용하면 테스트를 통과하지 않습니다.
그 이유는 화살표 함수는 자신만의 this 바인딩을 가지지 않으며, 함수가 정의된 상위 스코프의 this를 참조하기 때문입니다.
(이 경우 createNumber2가 가지는 this가 없어 전역 객체를 참조하게 되고 undefined가 반환됩니다.)
따라서 메서드 정의 시에는 일반 함수 표현식을 사용해야 합니다.
createNumber3
export function createNumber3(n) {
return {
value: n,
valueOf: function() {
return this.value;
},
toString: function() {
return this.value.toString();
},
toJSON: function() {
return `this is createNumber3 => ${this.value}`;
}
};
}
테스트 코드를 통과시키기 위해,
createNumber3 함수는 숫자가 아닌 객체를 반환해야 합니다. 하지만, 숫자 원시 값을 다룰 수 있어야 합니다.
객체 리터럴 방식을 사용하여 내부에 숫자 원시 값을 반환하는 value 속성을 추가했고,
createNumber3 객체의 value를 반환하는 toString과 valueOf 메서드를 정의합니다.
추가로, JSON.stringify 호출 시 특정 문자열을 반환하도록 toJSON 메서드를 추가했습니다.
CustomNumber
export class CustomNumber {
static instances = {};
constructor(n) {
if (n in CustomNumber.instances) {
return CustomNumber.instances[n];
}
this.value = n;
CustomNumber.instances[n] = this;
}
valueOf() {
return this.value;
}
toString() {
return this.value.toString();
}
toJSON() {
return this.value.toString();
}
}
주어진 테스트 코드를 통과시키기 위해,
CustomNumber 클래스는 숫자가 아닌 객체를 반환하지만, 숫자 원시 값을 다룰 수 있어야 합니다.
또한, 동일한 값을 가진 객체는 동일한 인스턴스를 반환해야 합니다.
먼저, CustomNumber 클래스를 정의하고, 생성자 내부에서 중복 인스턴스를 관리하기 위해
정적 프로퍼티 instances를 사용했습니다. 이를 통해 동일한 값을 가지는 인스턴스는 재사용됩니다.
그리고 valueOf, toString, toJSON 메서드를 정의하여 객체가 숫자처럼 동작하도록 했습니다.
createUnenumerableObject
export function createUnenumerableObject(target) {
const result = {};
for (const key of Object.keys(target)) {
Object.defineProperty(result, key, {
value: target[key],
enumerable: false,
});
}
return result;
}
테스트 코드를 통과시키기 위해,
createUnenumerableObject 함수는 입력받은 객체의 속성을 열거할 수 없는 속성으로 변경하여
새로운 객체로 반환해야 합니다. 객체의 속성에 직접 접근할 수 있지만, 열거(enumerate)하거나 복사(spread)할 때는 포함되지 않아야 합니다.
이를 위해, 입력받은 객체 target의 각 속성에 대해 Object.defineProperty를 사용하여 enumerable 속성을 false로 설정하고 열거할 수 없는 속성을 가진 새로운 객체를 반환하도록 했습니다.
forEach
export function forEach(target, callback) {
if (Array.isArray(target) || target instanceof NodeList) {
for (let i = 0; i < target.length; i++) {
const newValue = !isNaN(Number(target[i])) ? Number(target[i]) : target[i];
callback(newValue, i);
}
} else if (typeof target === 'object' && target !== null) {
const propertyArray = Object.getOwnPropertyNames(target);
for (let i = 0; i < propertyArray.length; i++) {
const key = propertyArray[i];
const value = target[key];
const newKey = !isNaN(Number(key)) ? Number(key) : key;
const newValue = !isNaN(Number(value)) ? Number(value) : value;
callback(newValue, newKey);
}
}
}
테스트 코드를 통과시키기 위해,
forEach 함수는 배열, NodeList, 그리고 객체를 순회할 수 있어야 합니다.
1. 배열 및 NodeList 처리:
- 입력 데이터가 배열 또는 NodeList인지 확인합니다.
- 배열과 NodeList는 인덱스를 사용하여 순회할 수 있기 때문에, for문을 통해 각 요소를 순회합니다.
- 각 요소의 값이 숫자로 변환 가능한 경우 숫자로 변환하여 처리합니다.
- 콜백 함수 callback을 호출하여 현재 값과 인덱스를 전달합니다.
2. 객체 처리:
- 입력 데이터가 객체인지 확인합니다.
- 객체는 key를 사용하여 순회하므로, Object.getOwnPropertyNames(target)를 사용하여 배열을 가져옵니다.
- getOwnPropertyNames()`메서드는 객체의 속성 중 열거 불가능한 속성까지 포함하여 순회할 수 있습니다.
- key와 값이 숫자로 변환 가능한 경우 변환하여 처리합니다.
- 콜백 함수 callback을 호출하여 현재 값과 key를 전달합니다.
map
export function map(target, callback) {
if (Array.isArray(target) || target instanceof NodeList) {
const result = [];
for (let i = 0; i < target.length; i++) {
const newValue = !isNaN(Number(target[i])) ? Number(target[i]) : target[i];
result.push(callback(newValue, i));
}
return result;
} else if (typeof target === 'object' && target !== null) {
const result = {};
const propertyArray = Object.getOwnPropertyNames(target);
for (let i = 0; i < propertyArray.length; i++) {
const key = propertyArray[i];
const value = target[key];
const newKey = !isNaN(Number(key)) ? Number(key) : key;
const newValue = !isNaN(Number(value)) ? Number(value) : value;
result[newKey] = callback(newValue, newKey);
}
return result;
}
}
테스트 코드를 통과시키기 위해,
map 함수는 배열, NodeList, 그리고 객체를 순회하여 각 요소에 대해 변화를 적용할 수 있어야 합니다.
(map 함수는 forEach 함수와 달리, 변환된 결과를 새로운 배열 또는 객체로 반환합니다.)
1. 배열 및 NodeList 처리:
- 입력 데이터가 배열 또는 NodeList인지 확인합니다.
- 배열과 NodeList는 인덱스를 사용하여 순회할 수 있기 때문에, for문을 통해 각 요소를 순회합니다.
- 각 요소의 값이 숫자로 변환 가능한 경우 숫자로 변환하여 처리합니다.
- 콜백 함수 callback을 호출하여 현재 값과 인덱스를 전달하고, 반환된 값을 결과 배열에 추가합니다.
- 결과 배열을 반환합니다.
2. 객체 처리:
- 입력 데이터가 객체인지 확인합니다.
- 객체는 key를 사용하여 순회하므로, Object.getOwnPropertyNames(target)을 사용하여 객체의 모든 속성 이름을 배열로 가져옵니다.
- key와 값이 숫자로 변환 가능한 경우 변환하여 처리합니다.
- 콜백 함수 callback을 호출하여 현재 값과 key를 전달하고, 반환된 값을 결과 객체에 추가합니다.
- 결과 객체를 반환합니다.
filter
export function filter(target, callback) {
if (Array.isArray(target) || target instanceof NodeList) {
const result = [];
for (let i = 0; i < target.length; i++) {
const newValue = !isNaN(Number(target[i])) ? Number(target[i]) : target[i];
if (callback(newValue, i)) {
result.push(newValue);
}
}
return result;
} else if (typeof target === 'object' && target !== null) {
const result = {};
const propertyArray = Object.getOwnPropertyNames(target);
for (let i = 0; i < propertyArray.length; i++) {
const key = propertyArray[i];
const value = target[key];
const newKey = !isNaN(Number(key)) ? Number(key) : key;
const newValue = !isNaN(Number(value)) ? Number(value) : value;
if (callback(newValue, newKey)) {
result[newKey] = newValue;
}
}
return result;
}
}
테스트 코드를 통과시키기 위해,
filter 함수는 배열, NodeList, 그리고 객체를 순회하여 각 요소에 대해 조건을 확인하고, 조건을 만족하는 요소만을 반환할 수 있어야 합니다.
1. 배열 및 NodeList 처리:
- 입력 데이터가 배열 또는 NodeList인지 확인합니다.
- 배열과 NodeList는 인덱스를 사용하여 순회할 수 있기 때문에, for문을 통해 각 요소를 순회합니다.
- 각 요소의 값이 숫자로 변환 가능한 경우 숫자로 변환하여 처리합니다.
- 콜백 함수 callback을 호출하여 현재 값과 인덱스를 전달하고, 조건을 만족하는 경우 결과 배열에 추가합니다.
- 결과 배열을 반환합니다.
2. 객체 처리:
- 입력 데이터가 객체인지 확인합니다.
- 객체는 key를 사용하여 순회하므로, Object.getOwnPropertyNames(target)를 사용하여 객체의 모든 속성 이름을 배열로 가져옵니다.
- key와 값이 숫자로 변환 가능한 경우 변환하여 처리합니다.
- 콜백 함수 callback을 호출하여 현재 값과 key를 전달하고, 조건을 만족하는 경우 결과 객체에 추가합니다.
- 결과 객체를 반환합니다.
every
export function every(target, callback) {
if (Array.isArray(target) || target instanceof NodeList) {
for (let i = 0; i < target.length; i++) {
const newValue = !isNaN(Number(target[i])) ? Number(target[i]) : target[i];
if (!callback(newValue, i)) {
return false;
}
}
return true;
} else if (typeof target === 'object' && target !== null) {
const propertyArray = Object.getOwnPropertyNames(target);
for (let i = 0; i < propertyArray.length; i++) {
const key = propertyArray[i];
const value = target[key];
const newKey = !isNaN(Number(key)) ? Number(key) : key;
const newValue = !isNaN(Number(value)) ? Number(value) : value;
if (!callback(newValue, newKey)) {
return false;
}
}
return true;
}
}
테스트 코드를 통과시키기 위해,
every 함수는 배열, NodeList, 그리고 객체를 순회하여 각 요소가 조건을 모두 만족하는지 확인할 수 있어야 합니다.
1. 배열 및 NodeList 처리:
- 입력 데이터가 배열 또는 NodeList인지 확인합니다.
- 배열과 NodeList는 인덱스를 사용하여 순회할 수 있기 때문에, for문을 통해 각 요소를 순회합니다.
- 각 요소의 값이 숫자로 변환 가능한 경우 숫자로 변환하여 처리합니다.
- 콜백 함수 callback을 호출하여 현재 값과 인덱스를 전달하고, 조건을 만족하지 않는 경우 false를 반환합니다.
- 모든 요소가 조건을 만족하는 경우 true를 반환합니다.
2. 객체 처리:
- 입력 데이터가 객체인지 확인합니다.
- 객체는 key를 사용하여 순회하므로, Object.getOwnPropertyNames(target)를 사용하여 객체의 모든 속성 이름을 배열로 가져옵니다.
- key와 값이 숫자로 변환 가능한 경우 변환하여 처리합니다.
- 콜백 함수 callback을 호출하여 현재 값과 key를 전달하고, 조건을 만족하지 않는 경우 false를 반환합니다.
- 모든 속성이 조건을 만족하는 경우 true를 반환합니다.
some
export function some(target, callback) {
if (Array.isArray(target) || target instanceof NodeList) {
for (let i = 0; i < target.length; i++) {
const newValue = !isNaN(Number(target[i])) ? Number(target[i]) : target[i];
if (callback(newValue, i)) {
return true;
}
}
return false;
} else if (typeof target === 'object' && target !== null) {
const propertyArray = Object.getOwnPropertyNames(target);
for (let i = 0; i < propertyArray.length; i++) {
const key = propertyArray[i];
const value = target[key];
const newKey = !isNaN(Number(key)) ? Number(key) : key;
const newValue = !isNaN(Number(value)) ? Number(value) : value;
if (callback(newValue, newKey)) {
return true;
}
}
return false;
}
}
테스트 코드를 통과시키기 위해,
some 함수는 배열, NodeList, 그리고 객체를 순회하여 각 요소가 조건을 하나라도 만족하는지 확인할 수 있어야 합니다.
1. 배열 및 NodeList 처리:
- 입력 데이터가 배열 또는 NodeList인지 확인합니다.
- 배열과 NodeList는 인덱스를 사용하여 순회할 수 있기 때문에, for문을 통해 각 요소를 순회합니다.
- 각 요소의 값이 숫자로 변환 가능한 경우 숫자로 변환하여 처리합니다.
- 콜백 함수 callback을 호출하여 현재 값과 인덱스를 전달하고, 조건을 만족하는 경우 true를 반환합니다.
- 모든 요소가 조건을 만족하지 않는 경우 false를 반환합니다.
2. 객체 처리:
- 입력 데이터가 객체인지 확인합니다.
- 객체는 key를 사용하여 순회하므로, Object.getOwnPropertyNames(target)를 사용하여 객체의 모든 속성 이름을 배열로 가져옵니다.
- key와 값이 숫자로 변환 가능한 경우 변환하여 처리합니다.
- 콜백 함수 callback을 호출하여 현재 값과 key를 전달하고, 조건을 만족하는 경우 true를 반환합니다.
- 모든 속성이 조건을 만족하지 않는 경우 false를 반환합니다.
shallowEquals
export function shallowEquals(target1, target2) {
if (target1 === target2) return true;
if (typeof target1 !== "object" || typeof target2 !== "object" || target1 === null || target2 === null) return false;
if ((target1 instanceof Number && target2 instanceof Number) || (target1 instanceof String && target2 instanceof String)) return false;
if (Array.isArray(target1) && Array.isArray(target2)) {
if (target1.length !== target2.length) return false;
for (let i = 0; i < target1.length; i++) {
if (target1[i] !== target2[i]) return false;
}
return true;
}
if (Object.getPrototypeOf(target1) !== Object.prototype && Object.getPrototypeOf(target2) !== Object.prototype) {
return Object.getPrototypeOf(target1) === Object.getPrototypeOf(target2);
}
const target1Keys = Object.keys(target1);
const target2Keys = Object.keys(target2);
if (target1Keys.length !== target2Keys.length) return false;
for (let key of target1Keys) {
if (!target2Keys.includes(key) || target1[key] !== target2[key]) return false;
}
return true;
}
deepEquals
export function deepEquals(target1, target2) {
if (target1 === target2) return true;
if (typeof target1 !== "object" || typeof target2 !== "object" || target1 === null || target2 === null) return false;
if ((target1 instanceof Number && target2 instanceof Number) || (target1 instanceof String && target2 instanceof String)) return false;
function isPrimitive(value) {
return value !== Object(value);
}
if (Array.isArray(target1) && Array.isArray(target2)) {
if (target1.length !== target2.length) return false;
for (let i = 0; i < target1.length; i++) {
if (!isPrimitive(target1[i]) && !isPrimitive(target2[i])) {
if (!deepEquals(target1[i], target2[i])) return false;
} else if (target1[i] !== target2[i]) {
return false;
}
}
return true;
}
if (Object.getPrototypeOf(target1) !== Object.prototype && Object.getPrototypeOf(target2) !== Object.prototype) {
return Object.getPrototypeOf(target1) === Object.getPrototypeOf(target2);
}
const target1Keys = Object.keys(target1);
const target2Keys = Object.keys(target2);
if (target1Keys.length !== target2Keys.length) return false;
for (let key of target1Keys) {
if (!target2Keys.includes(key) || !deepEquals(target1[key], target2[key])) {
return false;
}
}
return true;
}
advanced
import { createContext, useContext, useState, useRef, useEffect } from "react";
import { deepEquals } from "../basic/basic";
const memo1Map = new WeakMap();
export const memo1 = (fn) => {
if (!memo1Map.has(fn)) {
memo1Map.set(fn, fn());
}
return memo1Map.get(fn);
};
const memo2Map = new WeakMap();
const memo2DependenciesMap = new WeakMap();
const getSortedArray = (array) => {
return array.slice().sort();
};
export const memo2 = (fn, dependencies = []) => {
const dependenciesToString = JSON.stringify(getSortedArray(dependencies));
if (!memo2Map.has(fn)) {
memo2Map.set(fn, fn());
memo2DependenciesMap.set(fn, dependenciesToString);
} else if (memo2DependenciesMap.get(fn) !== dependenciesToString) {
memo2Map.set(fn, fn());
memo2DependenciesMap.set(fn, dependenciesToString);
}
return memo2Map.get(fn);
};
export const useCustomState = (initValue) => {
const [state, setState] = useState(initValue);
const customSetState = (newState) => {
if (!deepEquals(state, newState)) {
setState(newState);
}
};
return [state, customSetState];
};
const textContextDefaultValue = {
user: null,
todoItems: [],
count: 0,
};
export const TestContext = createContext({
value: textContextDefaultValue,
setValue: () => null,
});
export const TestContextProvider = ({ children }) => {
const contextValueRef = useRef(textContextDefaultValue);
const setValue = (key, newValue) => {
contextValueRef.current = { ...contextValueRef.current, [key]: newValue };
}
;
return (
<TestContext.Provider value={{ value: contextValueRef.current, setValue }}>
{children}
</TestContext.Provider>
);
};
const useTestContext = (key) => {
const { value, setValue } = useContext(TestContext);
const [state, setState] = useState(value[key]);
useEffect(() => {
setValue(key, state);
}, [state]);
return [state, setState];
};
export const useUser = () => useTestContext("user");
export const useCounter = () => useTestContext("count");
export const useTodoItems = () => useTestContext("todoItems");
2주차 과제제출 - 김초원 by kimfield98 · Pull Request #88 · hanghae-plus/front_2nd
github.com
3주차 후기가 궁금하다면?
[항해플러스 프론트엔드 2기] 3주차 후기 - 리액트 파헤치기
들어가며 안녕하세요 초원입니다. 👩🏻💻오늘도 멘토링 때 얻게 된 인사이트를 공유하며 시작해보려고 해요! 나는 이런 걸 잘 하는 사람이야. 고민을 통해 회사에 적용을 했고 이런 성과
kimfield.tistory.com
'항해플러스 프론트엔드 과정' 카테고리의 다른 글
[항해플러스 프론트엔드 2기] 중간점검, 솔직 후기! 과연 정말 실력이 늘었는지? (0) | 2024.07.22 |
---|---|
[항해플러스 프론트엔드 2기] 5주차 후기 - 디자인 패턴 (0) | 2024.07.22 |
[항해플러스 프론트엔드 2기] 4주차 후기 - 클린코드 (0) | 2024.07.16 |
[항해플러스 프론트엔드 2기] 3주차 후기 - 리액트 파헤치기 (0) | 2024.07.08 |
[항해플러스 프론트엔드 2기] 1주차 후기 - 리액트 파헤치기 (0) | 2024.07.07 |