정적 타입 검사란 ? 타입스크립트가 기본적으로 제공하는 기능으로,

컴파일 단계에서 변수나 리턴값, 파라미터 등의 타입에러를 찾아주는 것을 말한다. 

 

동적 타입 언어인 javascript는 암묵적 타입 변환 등을 허용하기 때문에 서로 다른 타입간의 계산을 허용하는 경우도 있고,

그로인해 예기치못한 런타임 에러가 발생할 수도 있다. 

 

그러나 타입스크립트는 컴파일시에 모든 변수, 매개변수 등의 정적 타입 검사를 진행하기 때문에, 

런타임시 발생할 에러를 사전에 방지할 수 있다. 

 

 

예를 들어, 

 

자바스크립트의 경우에는 

let num = 30;
let strNum = "2";

let plus = num+strNum;
console.log(plus); // "302" 출력 ;

let minus = num - strNum;
console.log(minus); //28 출력

암묵적 타입 변환을 허용해서 덧셈 연산인 경우, 숫자를 문자열로 변환한 계산을 시도하고, 뺄셈 연산의 경우엔 문자열을 숫자로 바꾸는 것을 시도한다. 

 

그러나 타입스크립트는 위와 같은 암묵적 타입 변환을 허용하지 않고, 코드에서부터 타입 에러를 표시해준다. 

 

또한, 

tsconfig.json 파일을 수정해서 더욱 엄격한 모드로 변경하는 것도 가능하다. (any를 허용하지 않음)

타입 추론이란, 

타입을 코드상에 명시적으로 지정하지 않아도 컴파일러가 변수 or 상수의 값을 자동으로 추론해주는 것을 말한다. 

 

예를 들어서, 

const x = 22;

const y = 10;

const z = x+ y ;

일때 컴파일러는 z의 타입이 number임을 자동으로 추론한다.

 

재할당이 가능한 let의 경우에는,

 

let MyName = "Ann";

MyName = 223; //MyName의 타입을 string으로 추론중이므로 에러 발생!

 

이것은 함수의 리턴값이나 객체의 타입을 추론하는 경우에도 동일하다. 

 

그러나 const와 let의 타입 추론 방식은 조금 다르다. 

 

상수인 const 는 리터럴 타입, 변수인 let은 원시 타입 추론이 이루어진다. 

 

여기서 리터럴 타입이란, string, number 와 같이 넓은 의미의 타입이 아니라, 

const x = 30의 경우 x는 30 이라는 구체적인 리터럴 타입으로 추론되는것을 말한다.  

 

원시타입은 우리가 일반적으로 아는 string, number, boolean, undefined 등 포괄적으로 사용되는 타입을 말한다. 

 

 

재할당이 가능한 let은 넓은 범위의 원시 타입으로 추론되지만. 재할당이 불가능한 상수 const는 리터럴 타입으로 추론되는 것이다. 

https://school.programmers.co.kr/learn/courses/30/lessons/340213?language=javascript

 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

 

처음에는 이렇게 풀었다. 

 

function solution(video_len, pos, op_start, op_end, commands) {
    var answer = '';
    const [min, sec] = pos.split(":").map(Number); 
    const [vmin,vsec]= video_len.split(":").map(Number); 
    const [smin,ssec]=op_start.split(":").map(Number); 
    const [emin,esec]=op_end.split(":").map(Number); 
    let [nmin,nsec] = [min,sec];
    const converter = (min,sec)=>min*60+sec;
    for(cmd of commands){        
        if(cmd==="next"){
            if(converter(nmin,nsec)>=converter(smin,ssec)&&converter(nmin,nsec)<=converter(emin,esec)){ 
               [nmin,nsec]=[emin,esec];
            }
                nsec += 10;
                if (nsec >= 60) {
                    nsec -= 60;
                    nmin += 1;
                }
            if(converter(nmin,nsec)>=converter(smin,ssec)&&converter(nmin,nsec)<=converter(emin,esec)){ 
               [nmin,nsec]=[emin,esec];
            }
        }else{
           if (nmin == 0 && nsec < 10) { 
                [nmin,nsec]=[0,0];
            } 
            else if(nsec<10){
                nsec = 60+nsec-10;
                nmin -=1;
            }else nsec = nsec-10;
        }
     
    }
    return nmin.toString().padStart(2, '0')+":"+nsec.toString().padStart(2, '0');
}

 

1,6,7,9,12  총 5개의 케이스에서 틀렸다고 나왔다. 

 

어떤 케이스가 고려되지 않았는지 모르겠어서 질문 게시판을 찾아보니

동영상 길이보다 길어지는 경우를 고려해야 한다고 나와있었다!!!

 

애초에 props로 동영상 길이를 왜 제공해주는지 생각했어야 했는데....ㅎㅎ 나의 실수 

 

그래서 중간에 

 if(converter(vmin,vsec)<=converter(nmin,nsec)) [nmin,nsec]=[vmin,vsec];

이 부분을 추가해 주었는데 어라라 아직도 1번케이스가 통과가 되지 않는 것이었다. 

 

다시 찾아보니 prev 명령어의 경우에도 오프닝 구간안에 있으면 오프닝 끝으로 이동해야한다는 것이었다.........헉 

function solution(video_len, pos, op_start, op_end, commands) {
    var answer = '';
    const [min, sec] = pos.split(":").map(Number); 
    const [vmin,vsec]= video_len.split(":").map(Number); 
    const [smin,ssec]=op_start.split(":").map(Number); 
    const [emin,esec]=op_end.split(":").map(Number); 
    let [nmin,nsec] = [min,sec];
    const converter = (min,sec)=>min*60+sec;
    for(cmd of commands){        
        if(cmd==="next"){
            if(converter(nmin,nsec)>=converter(smin,ssec)&&converter(nmin,nsec)<=converter(emin,esec)) 
               [nmin,nsec]=[emin,esec];
            
                nsec += 10;
                if (nsec >= 60) {
                    nsec -= 60;
                    nmin += 1;
                }
            if(converter(vmin,vsec)<=converter(nmin,nsec)) [nmin,nsec]=[vmin,vsec];
            else if(converter(nmin,nsec)>=converter(smin,ssec)&&converter(nmin,nsec)<=converter(emin,esec)){ 
               [nmin,nsec]=[emin,esec];
            }
        }else{
           if (nmin == 0 && nsec < 10) { 
                [nmin,nsec]=[0,0];
            } 
            else if(nsec<10){
                nsec = 60+nsec-10;
                nmin -=1;
            }else nsec = nsec-10;
            
             if(converter(nmin,nsec)>=converter(smin,ssec)&&converter(nmin,nsec)<=converter(emin,esec)) 
               [nmin,nsec]=[emin,esec]; //여기 이부분을 추가함 
        }
     
    }
    return nmin.toString().padStart(2, '0')+":"+nsec.toString().padStart(2, '0');
}

 

오프닝 구간인지 확인하는 조건문을 prev 에서도 넣어주니 

간단하게 해결이 되었다. ㅎㅎ 

난이도 1이라 시간제한이 빡센 문제는 아닌 듯 하다 

 

끝 ~.~

window에서 vscode를 사용할 때 발생하는 에러인데 
윈도우에서만 줄 끝의 문자 포멧이 CRLF로 다르기 때문이다. (맥/리눅스는 LF)
로드가 안 되는 에러는 아니지만 코드를 편집할때 무척 거슬린다. 
 
 
해결법은 
.eslintrc.js 파일 내부 rule 안에 
이렇게 추가하면 해결된다. 

rules: {
 		.
 		.
 		.
    "prettier/prettier": ["error", { "endOfLine": "auto" }] //이 줄을 추가해주자 
  },

 
 
매우 간단 ~.~ 

최근 다시 면접을 보러다니면서 

풀스택으로 전환할 생각은 없나요? 왜 프론트엔드를 선택했나요 라는 질문을 많이 받았다.  

 

지금은 내 연차가 길지 않아 프론트엔드 포지션을 고수할 수 있지만, 계속 일을 하고 시니어급이 되었을때

과연 프론트엔드 하나만으로 경쟁력을 유지하기는 어렵지 않을까? 라는 생각이 들었다. 

나의 경쟁력을 높이고, 또 나의 미래의 선택지를 넓히기 위해서는 프론트 뿐만 아니라 백엔드쪽의 역량도 키워야한다는 결심이 섰다. 

 

 

처음 타입스크립트를 공부할 때 nest.js 를 조금 써봤지만, 그 이후로 프론트엔드 개발자로 일하면서 백엔드개발을 경험할 기회가 거의 없었다. 마침 시간도 많이 생겼으니 지금이 백엔드 공부를 할 수 있는 적기가 아닐까? ^^...

 

 

 

node.js나 express는 미들웨어 서버를 구성해보면서 조금 써봤는데 아무래도 너무 제약이 없다보니 나에게는 더 불편하게 느껴졌다.

예를들면, 로컬에서는 잘 돌아가지만 실제로 빌드를 할 때는 예상치 못한 오류들이 발생한다던가...

 

 

그래서 제약사항이 많고 구조적으로 node.js를 사용할 수 있는 프레임워크인 nest.js를 다시 선택했다 

 

https://nestjs.com/

 

NestJS - A progressive Node.js framework

NestJS is a framework for building efficient, scalable Node.js web applications. It uses modern JavaScript, is built with TypeScript and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Progr

nestjs.com

 

오늘부터 강의도 조금씩 들어보면서 놓고있던 백엔드 개발 감각을 되찾아야겠다. ~,~ 

나는 주로 업무를 할 때 styled-component와 emotion을 사용해 컴포넌트들을 관리해왔다. 

props를 넘겨줄 수 있어서 동적인 스타일을 적용하기 편리하고, 러닝커브가 낮아서 쉽게 접할 수 있었기 때문이다. 

 

그러나 최근들어 tailwind css를 쓰는 기업들이 많아지면서, 과연 어떤 부분이 css in js 보다 더 좋은지, 혹은 단점은 없는지 알고싶어졌다. ~,~ 

 

 

tailwind 사용방법은 공식 문서를 참고하자 

 

https://tailwindcss.com/docs/installation

 

Installation - Tailwind CSS

The simplest and fastest way to get up and running with Tailwind CSS from scratch is with the Tailwind CLI tool.

tailwindcss.com

 

 

아래에 내가 알게된 둘의 장단점과 차이에 대해 정리해보겠다. 

 

  • CSS in JS 
    • javascript 파일 내에서 스타일을 정의해서 사용한다. 대표적으로 Styled-component, Emotion이 있다. 
    • styled의 경우 const MyComponent = styled.div`...`; 태그를 변수 형태로 정의해서 사용한다.
    • emotion의 경우 const MyStyle = css``;  => <div css={MyStyle}/> css를 변수로 정의해서 css라는 속성으로 넣어준다.
    • props,state에 따라 간단하게 동적인 스타일을 적용할 수 있다. 
    • 러닝커브가 낮아서 쉽게 사용할 수 있다. 
    • 랜더링 시 javascript 파일 안에 style 태그로 들어간다. 
    • js 코드가 업데이트될때마다 함깨 렌더링 되기 때문에 랜더링 속도가 더 오래걸릴 수 있다. (일부 스타일은 브라우저에서 캐싱되기도 함)

 

 

  • Tailwind CSS
    • 랜더링 시 단일 css파일로 동작해 js 코드의 리랜더링 등에 영항을 받지 않는다. html에서 직접 css를 참조. 
    • 정적 css파일로 동작하기때문에 동적인 스타일을 적용하기위해서는 여러 클래스를 조건에따라 관리해야하는 불편함이 있다. (불가능한것은 아님! classnames 로 조건에 따른 복수의 클래스네임을 관리할 수 있다.)
    • 클래스기반의 캐싱을 사용 class name 안에 <a href="#" class="text-blue-500">+ 198 others</a> 기본적으로 제공하는 스타일을 넣거나,
    • 이렇게 커스텀 스타일을 정의해서 사용하는 것도 가능하다.  
module.exports = {
  theme: {
    colors: {
      'blue': '#1fb6ff',
      'pink': '#ff49db',
      'orange': '#ff7849',
    },
  }
}
------------------------------------------------
<div class="bg-blue text-orange p-4">
  custom text
</div>

   

 

우선 기본적으로 tailwind에서 제공하는 유틸리티 클래스 구조와 rem 단위에 익숙해질 필요가 있겠군... 

 

e.g ) className-"p-4" 는 위아래좌우에 1rem(보통 16px)의 패딩을 넣는다는 뜻,

        className="py-2.5 px-3"은 위아래 10px, 좌우 12px의 패딩을 넣는다는 뜻. 

 

       (1: 0.25rem  ==  4px)

커스텀 훅은 React 에서 기본적으로 제공하는 기본적인 훅(useState, useEffect, useCallback, useMemo, useContext 등)과 비슷한 방식으로 작동하는 사용자 정의 hook 이다.

 

useState나 useEffect를 내장하여 기능을 확장하거나, 프로젝트 전체에서 반복적으로 사용되는 로직 등을 모듈화하여 여러 컴포넌트에서 재사용할 수 있도록 하는 것이 커스텀 훅의 목적이다. 

 

 

[커스텀 훅을 쓸 때의 장점 ]

 

-  반복적으로 사용되는 로직의 코드 반복을 줄이고, 재사용성을 높일 수 있다.

:예를 들어 동일한 데이터를 여러 컴포넌트에서 사용해야 하는 경우, 데이터를 불러오는 로직을 커스텀훅으로 분리하면 된다.

 

-  UI를 구성하는 부분과, 비즈니스 로직을 구성하는 부분이 분리되어 유지보수가 용이해지고 공동 작업에 유리해진다. 

: 데이터를 요청하거나 수정작업하는 부분을 별도의 custom hook 파일로 분리하면 코드 분량 자체가 간결해져서 유지보수가 편하고 여러 사람이 동일한 코드를 반복해서 작성할 필요가 없어진다. 

 

 

 

그리고 커스텀 훅을 작성하기 위해서는 몇가지 규칙이 있다. 

 

1. 훅의 이름은 반드시 use로 시작해야한다. 

const useMyHook = ()=>{

//커스텀 훅 안에서 useState,useEffect등을 사용하는 것도 가능하다! 

return ..

}

 

 

2. 반복문이나, 조건문 안에서 사용해서는 안된다. 

.
.
if(...){
useMyHook(); //조건에 위배됨
}

for(... of ..){
useMyHook() // 이것도 안 됨! error 발생  
}

올바른 방식으로 작성된 커스텀 훅은 리액트에서 useState 와 같이 리액트 훅으로 인식되기때문에 

반복문과 조건문 안에서 사용할 경우 에러가 발생한다. 

 

 

3. 커스텀 훅을 사용하는 각 컴포넌트는 독립적인 상태를 가진다. 커스텀 훅을 호출하는 컴포넌트마다 별도의 state가 생성되고, 그 state들은 서로 값을 공유하지 않는다. 

 

그 예시로 간단한 카운터 커스텀 훅을 작성해봤다. 

export const useMyCounter = () => {
  const [count, setCount] = useState(0);

  const increment = () => {
    setCount(count + 1);
  };

  const decrement = () => {
    setCount(count - 1);
  };

  
  return { count, increment, decrement };
};
/////////////////////////////////////////////////
const AComponent = () => {
  const { count, increment, decrement } = useMyCounter();

  return (
    <div>
      <span>값: {count}</span>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
    </div>
  );
};

const BComponent = () => {
  const { count } = useMyCounter();

  return (
    <div>
      <span>값: {count}</span>
    </div>
  );
};

 

이 카운터 훅을 A컴포넌트와 B컴포넌트에서 각각 사용하는 경우, 

A 컴포넌트에서 버튼을 통해 값을 증가하거나 감소시켜도 B컴포넌트의 counter state에는 영향을 미치지 않는다. 

 

 

 

 

커스텀 훅을 사용하면 좋은 예시의경우는 우선 아래 문서를 참고하면 좋다

(추후 카운터보다 더 좋은 예시를 첨부하겠음.. )

 

https://ko.react.dev/learn/reusing-logic-with-custom-hooks#extracting-your-own-custom-hook-from-a-component

 

커스텀 Hook으로 로직 재사용하기 – React

The library for web and native user interfaces

ko.react.dev

 

 

 

또는 React-query 등 라이브러리를 통해 서버 상태를 커스텀 훅으로 관리하는 방법도 있다.

 

리액트 쿼리는 자체 커스텀 훅을 제공하는 상태관리 라이브러리로 주로 대규모 프로젝트에서의 api통신과 비동기 데이터 처리를 편리하게 해주는 도구이다. 

 

useQueryClient 라는 훅을 통해 쿼리 객체를 선언하고, 

useQuery로 데이터 fetch 작업을, useMutation로 데이터의 수정과 삭제, 생성 작업을 수행한다. 

 

 

https://house-of-ham.tistory.com/entry/React-Query%EC%99%80-Axios%EB%A1%9C-%EB%B9%84%EC%A6%88%EB%8B%88%EC%8A%A4-%EB%A1%9C%EC%A7%81%EC%9D%84-%EC%B5%9C%EC%A0%81%ED%99%94-%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95-feat-%EC%84%B1%EB%8A%A5%EC%B5%9C%EC%A0%81%ED%99%94-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%B2%98%EB%A6%AC

 

React Query와 Axios로 비즈니스 로직을 최적화 하는 방법 (feat. 성능최적화, 데이터 처리)

백오피스 시스템을 개발하면서 복잡한 기능 구현과 데이터 처리가 큰 도전과제였다. 특히 대용량 데이터를 효율적으로 관리하고, 비즈니스 로직을 유연하게 확장하며, 일관된 방식으로 유지해

house-of-ham.tistory.com

자세한 사용법은 이 글을 참고하길! 

 

 

 

 

 

 

 

 

타입스크립트를 사용하는 프론트앤드 개발자라면 반드시 사용하게되는 type과 interface ! 

둘의 차이를 알고 적재적소에 사용한다면  안정적이고, 유지보수가 용이한 높은 품질의 코드를 작성할 수 있게 될 것이다

 

 

interface 

'객체' 의 구조를 정의하는데 사용된다. 클래스기반의 코드를 작성하는데 주로 사용되며 병합이 가능해 확장 가능성이 있는 타입을 정의할 때 용이하다. 객체의 타입만 정의 가능. 

 

interface의 자동 병합 예시로는 

 

interface Family {
mother: string;
father: string;
}

interface Family {
daughter: string;
son :string;
}

 

이렇게 Family 인터페이스를 두 번 정의할 경우 

첫번째 내용에 두번째 내용이 덧씌워지거나 오류가 발생하지 않고,

 

interface Family{
mother:string;
father:string;
daughter:string;
son:string
}

이렇게 하나로 병합된다는 특징을 가지고 있다. 

 

 

 

type

객체 뿐만 아니라 모든 요소의 타입 정의가 가능하다. 유니온 타입이나, 컴포넌트의 props, state, 이벤트 등의 타입도 정의가 가능해서 React 환경 개발에서는 type을 주로 사용한다. 

 

**유니온 타입 : 여러 타입 중 하나를 선택하는 것

type Info = { phoneNum : string | number; }  //유니온 타입 예시

 

 

interface와 type은 확장 방식도 다르다. 

 

//interface의 경우

interface Member {
name:string;
age: number
}

interface Student extends Member {
stuId:number;
major:string;
}


//type 의 경우

type Member = {
name:string;
age: number
}

Type Teacher = Member & {
tcId:number;
major:string;
}
}

 

 

 

+ Recent posts