기억의 실마리
2024. 10. 20. 18:46

🤔NextJS 쿠키?

브라우저에 저장되는 일반적인 쿠키와 다를게 없지만

NextJS는 SSR을 지원한다는 점에서

cookie를 서버에서 확인할 수 있는 방법이

제한적이기 때문에 CSR(React 등)만 활용하여

개발하던 개발자들은 생각보다 더 난해한 문제를

겪게 될 수 있다.

 

그런 개발자 중 한명이 필자였고 특히

쿠키를 활용한 JWT 로그인 유지를 구현할 때

interceptor를 통해 매번 토큰을 header에 넣어 요청하고

accessToken이 만료된 경우에는 refreshToken을

서버에 재발급요청하여 재생성된 토큰들을 쿠키에 저장하고

저장된 쿠키를 통해서 이전 요청을 재요청하는 프로세스에서

accessToken 쿠키가 undefined인 상태로 요청이 되는 이슈

꽤나 험난한 삽질과 많은 시간을 고민하게 되었다.

 

결과적으로 NextJS를 제대로 다루기 위해서는

hydration,

middleware,

server - component,

server - actions / mutations,

위 4가지의 작동방식은 필수적으로 학습을 통해

Next가 작동되는 방식에 대해 이해하고 있어야한다는

생각이 들었다.

 

🫗hydration

(hydration에 대한 글은 개인적 주관이 강한 글이기 때문에 넘어가셔도 좋습니다😊)

필자는 단순히 하이드레이션을 주입식 교육처럼

하이드레이션 = 서버의 결과를 클라이언트로 합치는 것

이라고만 생각했지만 결과적으로 이해하게 된 계기는

여러 이슈를 겪으며 어원에 대해 다시 생각하는 것으로

이해도를 갖추게 된 것 같다.

 

hydration이란 수분공급 이라는 어원을 가지고 있다.

그렇다면 어째서 수분공급이지? 라는 의구심을 가지고

좀 더 단순하게 어원에 대해 생각했을 때 식물이 뿌리에서

흡수한 물을 줄기를 통해서 수분공급을 하는 방식을 떠올려 봤다.

 

뿌리 = Server

(땅속에 있어 우리가 표면적으론 볼 수 없지만 핵심이 되는 뿌리)

 

줄기, 잎, 꽃 등 = Client

(땅을 파지 않아도 겉으로 볼 수 있는 식물의 외형)

 

식물은 수분공급이 제대로 이루어지지 않는다면

그 식물은 시들어버리거나 제대로 성장하지 못한다.

 

그렇기 때문에 Server에서 보여주고자 하는 것을

Client에 공급해주어 제대로된 결과물을 보여주는 것이

Hydration이구나! 라는 결론을 내리게 되었다.

 

물론 그저 나의 상상력으로 여기까지 온 것일 수도 있다...

하지만 좀 더 어원에 대해 이해하려할 수록

이해도가 높아지는 경우가 생각보다 많았던 것 같다.

 

만약 영어가 아닌 한글이었다면 조금 더 직관적으로

프로세스를 이해했을 수도 있지만 프로그래밍 언어가

기본적으로 영어로 되어있기 때문에 우리가 프로그래밍에

대해서 이해하기 어렵게하는 하나의 벽이 되고있지 않나...

하는 생각이 들곤 한다.

 

🔗middleware

middleware는 server에서 client에 렌더링되기 이전에

거치게 되는 곳으로 서버와 클라이언트의 교집합 지점이라고

볼 수도 있는 영역이다. 때문에 NextJS를 다룬다면 정말 중요한

기능이기 때문에 꼭 알아야된다고 생각하는 것 중 하나다.

 

NextJS를 활용해서 사용자에게 보여주기전에 server에서

내부적으로 redirect하거나 필수적인 쿠키를 미리 설정

가능하고 국제화를 하는 경우에는 페이지에 접속한

국가에 적합한 언어로 구성된 페이지를 제공하기 위해서

활용하기도 한다.

 

middleware 활용처는 너무 많기 때문에 예제코드는

필자의 깃허브 링크로 남겨두겠습니다.

 

* nextjs14 app-router 국제화를 적용한 템플릿을 예제로 넣었습니다.

Github: https://github.com/zeriong/next14-app-router-i18n-template/blob/master/middleware.ts

 

⚙️server-component

NextJS에서 컴포넌트를 구성할 때 최상단에 "use client"를

작성하지 않는다면 기본적으로 server-component로 작성된다.

그리고 "use client"를 작성한다면 react에서 사용되는 CSR관련

Hooks를 활용할 수 있게 된다.

 

단 여기서 중요한 것은 "use client"라고 해서 SEO가 안되는 것은

아니라는 점이다.

 

html 구성은 server에서 렌더링이 되기 때문에 SEO는

영향을 끼치지 않는다. 단지 CSR에서 활용 가능한

React의 Hooks를 활용해서 CSR에서 작동되는 JS를 활용하겠다.

라고 선언하고 활용하는 것에 불과하다.

 

그러니 "use clinet"를 썼다고 해서 SEO가 안될 것이라는

걱정은 하지 않아도 될 것이다.

(useEffect를 활용하여 state 변경후에 생성되는 컴포넌트 제외)

 

 

🪄server - actions / mutations

서버에서 실행되는 비동기함수를 뜻한다.

form에서 활용하는 action이나 기타 fetch를 활용한

api요청 등을 server에서 작동하게 만드는 것이다.

 

활용방식은 아주 간단하게 해당 함수의 환경 최상단에

"use server" 를 선언하게 되면 이는 server에서 작동하는

함수로 정의된다.

 

"use server"를 정의하는 환경은 주로 파일단위나

함수단위로도 활용 가능하다.

 

상황에 따라서 우리는 다양하게 활용하지만 주로

하나의 파일에서 "use server"를 최상단에

정의하여 모듈화하여 활용하는 경우가 보편적이다.

 

예시적으로 actions.ts 파일을 생성한다고

가정했을 때의 예제 코드이다.

 

  • server-actions 예제코드
// actions.ts

"use server";

/**@desc login server-action */
export async function loginAction(prevState: { success?: boolean; msg?: string }, formData: FormData) {
  const userId = String(formData.get("userId")).trim() as string;
  const userPassword = String(formData.get("userPassword")).trim() as string;

  if (!userId.length || !userPassword.length) return { success: false, msg: "아이디 또는 비밀번호를 확인해주세요" };

  const reqBody = { userId, userPassword };

  const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL_AUTH}/auth/login`, {
    method: "POST",
    body: JSON.stringify(reqBody),
  });

  const resBody = await res.json();

  if (resBody.code != 200) return { success: false, msg: resBody?.message };

  return { success: true, msg: "success" };
}

 

위처럼 server-action을 구성하고 해당 server-action을 로그인을 하는 컴포넌트에서 useFormState 훅을 활용해서 state로 response 결과값을 client에서 동적으로 확인할 수 있다.

 

  • server-mutations 예제코드
// mutations.ts

"use server";

import { getUserInfo } from "@/apis/user";

/** get user data */
export async function getUserData() {
  const res = await getUserInfo();
  return res.body.data;
}

 

정말 단순하게 "use server"를 명시해주어 server에서 해당 api로 fetch를 통해 response 값을 받아볼 수 있다.

 

server - actions / mutations 장점

  1. network 요청 수를 줄일 수 있다
  2. server 내부에서 미리 값을 fetch 받아 렌더링하기 때문에 SEO 향상
  3. API route 없이 서버 측에서 직접 DB에 접근가능
  4. 민감한 데이터를 클라이언트 측에 노출시키지 않을 수 있음
  5. client side 번들 사이즈가 줄어 초기 로딩 속도 개선

 

🍪NextJS 쿠키 트러블 슈팅

내가 맞닥뜨린 이슈는 결국 NextJS에서 쿠키에 대한

hydration이 필요한 상황이었고 가장 첫번 째로 고민한 것은

가능한 것에 대한 고찰이었다.

 

서버에서 쿠키에 접근가능한 방식을 생각해봤을 때

next header에 접근할 수 있어야하는 것이

우선 선행이 되어야 했다.

 

그렇다면 어떠한 경우에 server에서 쿠키에 접근할 수 있는가?

내가 떠올릴 수 있는건 두가지였다.

 

1. middleware의 NextRequest를 통한 접근

2. server-component에서 next-header의 cookies()로 접근

 

1번의 경우는 현실성이 부족했고 2번으로 접근하기로 했다.

 

하지만 interceptor를 구성할 때 따로 api를 요청할 때 마다

지속적으로 재사용해야 하기 때문에 파일로 분기하여

구성할 수 밖에 없었다.

 

단순히 fetch에 interceptor를 구성하여 붙이는 코드로 구성되어있어

최상단에 "use server"를 명시하는 것도 말이 안되고,

그렇다고 interceptor를 server-component에서 구성하는 것도

말이 안된다고 생각이 들었다.

 

그때 마침 생각난 것이 바로 server-component가

next-header에 접근할 수 있는 이유에 대해서 생각해봤다.

 

nextjs14에서는 컴포넌트 구성 시 최상단에

"use client"를 명시하지 않으면 서버컴포넌트로 정의된다.

 

이는 즉 이전 버전에서 사용하던 "use server"가 생략됐을 뿐이고

server - actions / mutations에서 최상단에 "use server"를

명시하고 활용하는 것과 동일하다고 생각이 들었다.

 

그렇다면 방법은 하나 뿐이다.

utils에 server 폴더를 만들고 최상단에 "use server"를 명시한

cookies.ts파일을 만들어 내부에 유틸을 구성하는 방법이다.

 

getServerSideCookie,

setServerSideCookie,

removeServerSideCookie,

 

위 세가지 쿠키관련 유틸을 구성하고

next-header에 접근할 수 있는 환경에서

쿠키를 컨트롤할 수 있도록 만들어주었다.

 

결과는 성공적이었고,

모든 server - actions / mutations와

클라이언트에서의 fetch 요청으로도 성공적인

로그인 유지가 가능한 결과를 얻을 수 있었다.

 

✍️마치며...

개발에 대한 경험이 늘어남에 따라 느끼는 것은 개발은 언제나 정답이 정해져있는 것이고 그 정답에 도달하기 위해선 내가 맞닥뜨린 문제에서 성공적인 예시를 통해 하나씩 이어가는 것이 가장 빠르고 올바르게 이해하며 정확하게 문제를 해결할 수 있는 방법인 것 같다.

2024. 10. 13. 17:28

⌨️단축키 리스트

< 코드 관련 단축키 >

Ctrl + W : 텍스트 확장 선택
Ctrl + D : 선택된 블럭을 하단에 복제
Ctrl + Y : 라인 삭제
Ctrl + Shift + Alt + J : 모든 같은단어 선택
Ctrl + Alt + L : 파일 단위 재정렬
Ctrl + Alt + T : 목록중 코드 감싸기
Ctrl + Shift + ] / [ : 가장 가까운 괄호 시작/종료로 이동
Ctrl + Shift + M : 괄호이동

 

Ctrl + R : 파일단위 지정 글자 replace

Ctrl + Shift + R : 프로젝트 단위 지정 글자 replace


F2 : 워닝이동
Alt + J : 같은단어 차례선택
Shift + F6 : 이름 일괄 변경 하기
Ctrl + F6 : 함수 전체 변경 - 모든것
Ctrl + Shift + backspace : 마지막 작성 코드이동
Alt + Shift + 위,아래: 라인 이동
Alt + 위,아래 : 메소드 단위로 커서 위치 이동

F4 : 정의보기
Ctrl + P : 함수호출시 인수 정보 확인
Ctrl + Alt + F7 : 함수호출하는 곳 찾기
Ctrl + F7 파일내 호출찾기
Ctrl + Alt + F7 모든파일검색
Ctrl + G : 라인번호 이동

 

< 네비게이트 관련 단축키 >

Ctrl + Shift + F : 전체 검색
Ctrl + Shift + N : 파일검색
Ctrl + Shift + Alt + N : 메소드 검색
Ctrl + E : 최근 열었던 파일 목록
Ctrl + F12 : 함수검색

Ctrl + Alt + Shift + T 밑의 팝업 Extract Method
Alt + Delete : 안전한 변수명등 삭제
Ctrl + F6: 함수파라미터 안전 추가
Ctrl + Alt + N : 임시변수에 담긴 값을 값자체로 하나로 단축
Ctrl + Alt + V : 변수만들기
Ctrl + Alt + P : 함수내 하드코딩된 값 파라미터 만들기
Ctrl + Alt + M : 블럭지정하여 함수 작성

 

단축키 출처: https://k-developer.gitbook.io/dev/ide/webstorm/undefined-1

 

 

⭐핵심 단축키

물론 필자 기준이긴 하지만... 아마 보편적으로 많이들 사용하는 단축키라고 생각이든다.

(Ctrl + F, Ctrl + C or V 등은 제외...)

 

Ctrl + D : 선택된 블럭을 하단에 복제

=> 비슷한 유형의 코드 하단에 작성 시 활용

 

Ctrl + W : 텍스트 확장 선택

=> 커서가 있는 텍스트를 일괄 선택할 때

 

Ctrl + E : 최근 열었던 파일 목록

=> 파일 텝을 모두 닫더라도 history를 통해 다시 열 수 있어 유용

 

Ctrl + Tab : 최근 열었던 파일 스왑

=> 특정 파일을 참고하며 만들 때 스왑하며 사용

 

F2 : 워닝(에러) 이동

=> 상단부터 차례로 리팩토링할 때

 

Commit Diff 창에서 F4 : 해당 파일로 이동

=> commit하기 전 diff를 체크하다 수정이 필요한 경우 즉시 파일로 이동

 

Shift + Shift : 파일명, 변수, 함수명 등 전체 검색

=> 주로 파일명을 검색하여 파일을 열고 싶을 때 사용

 

Ctrl + R : 파일단위 지정 글자 replace

=> 네이밍 리팩토링 시 일괄적용을 위해 사용

 

Ctrl + Shift + R : 프로젝트 단위 지정 글자 replace

=> 프로젝트 단위로 지정 이름을 변경할 때 사용

 

Shift + F6 : 이름 일괄 변경 하기

=> 파일, 함수, 변수 등 모든 이름 변경할 때

2024. 10. 13. 16:27

🤔서론과 계기

회사에서 진행하는 프로젝트에서 useHookForm을

활용 가능하도록 input 공용 컴포넌트를 만들다가

tailwindcss를 통해 스타일을 지정하다"focus:", "hover:" 문득

특정 문자열을 반드시 포함하는 타입을 만들면 어떨까?

라는 생각과 함께 고민해보고 만들어보게 되었다.

 

어떻게 만들면 그게 가능할까 라고 고민하는 순간

가장 먼저 템플릿 리터럴이 떠올랐고 단순하게 작성해봤는데

다행이 해매지않고 원하는 결과를 얻었다.

 

typescript로 고생하는 누군가에게 이 포스팅이 도움이 됐으면 좋겠다.

 

  • 예제코드
// interface 예제
interface IHolder {
  inputPlaceholder?: `placeholder-${string}`;
}

// type 예제
type TFocus = `focus:${string}`;

 

✍️마치며...

개발을 할 때 무언가 필요할 때 검색을 하기전 스스로 떠올려 만들어보는 것이 무엇보다 중요한 것 같다. 정보가 넘쳐나는 시대이기에 더욱 스스로 사고하는 기회가 적어지게 되는 것 같고 이는 개발자에게 치명적이지 않을까? 라는 생각을 하게된다... 앞으로도 초심을 잃지않고 사고력을 꾸준히 키워나아가자!

'Frontend > TypeScript' 카테고리의 다른 글

[ Typescript ] 타입스크립트  (0) 2023.02.15
2024. 10. 6. 15:51

🎯Dart

Dart란 구글에서 만든 프로그래밍 언어로

객체지향 언어로 Flutter 개발에 특화된 언어이다.

Flutter는 멀티 플랫폼 대응이 뛰어난

프레임워크로 손꼽힌다.

 

JIT(Just-In-Time), AOT(Ahead-Of-Time)

컴파일 방식을 모두 사용하며

null safety를 지원한다.

 

개발중에는 JIT 컴파일방식으로 Hot-Reload가

가능하게 되어 빠른 속도로 변경사항을 확인 가능하고

 

배포할 때는 AOT 컴파일방식으로

실행이전에 컴파일을 완료하고 기계어를 전달하여

빠른 실행속도를 제공한다.

 

🤔Dart에 대한 견해

한국 서버구성 언어 점유율 1위 객체지향 프로그래밍인

JAVA와 유사하다는 느낌을 강하게 받았다.

 

최근에 공부하고 있던 JAVA언어와 생각보다

너무 유사해서 오히려 러닝커브가 덜 느껴진 것 같다.

 

같이 근무하고 있는 멋쟁이 JAVA 개발자가

Flutter를 공부할까 고민한 바 있었는데

적극 추천해도 무리가 없을 것 같다.

 

또한, 구글에서 만든 언어이기도 하고

Android, IOS 크로스플랫폼 일치율이 가장

높다고 언급되는 Flutter 개발도 기대가 된다.

 

  • 예제코드
void main() {

  /** ------------------------------  타입과 선언  -------------------------------- */

  // 변수 선언 시 var로 선언하는 것은 관습적으로 함수, 메서드 등 내부의 지역변수로서 선언할 때 많이 사용됨
  var name = "zeriong";
  name = "제리옹";

  // class내부에서 변수나 property의 경우 아래 처럼 타입 명시 선언을 활용한다고 함.
  String realName = "????";

  // dynamic type 은 어떤한 타입이라도 넣을 수 있다.
  // typescript 에서 활용되는 any와 같은 타입으로 볼 수 있다.

  // dynamic을 타입 명시로 활용하는 예
  dynamic anyType = "sdf";
  anyType = 123;
  anyType = false;
  anyType = {};

  // 아무것도 할당하지 않고 선언만 한 경우 자동으로 dynamic으로 선언된다.
  var dynamicVar;
  dynamicVar = 123;
  dynamicVar = true;
  dynamicVar = [];
  dynamicVar = "하이";

  // 하지만 다이나믹 타입으로 지정되어 있으면 해당 변수에 대해 추론이 안되어
  // 아래처럼 조건문을 추가해준다면 해당 변수에서 접근가능한 dart에서 자체 제공하는 매서드, api들을 활용할 수 있다.
  if (dynamicVar is String) {
    print("dynamicVar length is " + dynamicVar.length.toString());
  }

  /** 이상적으로 dart에서 dynamic 사용을 지양하는 것이 좋다. */

  print("hello, my name is " + name);

  // Null albe 선언 (typescript의 ?를 사용하는 옵셔널 체이닝과 같음)
  String? alias; // string 또는 null이 가능한 형태
  alias = "potato";
  alias = null;
  // 위에서 활용한 dynamic 타입과 마찬가지로 if문을 통해서 타입을 가정할 수 있어야 제공 메서드, api 활용 가능하다.
  if (alias != null) {
    alias.isNotEmpty;
  }
  // 또는 아래 처럼 활용 가능
  // typescript에서 활용할 때 처럼 있다면 이어서 실행될 것이고, 아닌 경우 그대로 멈추게 된다.
  alias?.isNotEmpty;
  alias?.length;

  // final을 활용한 상수 지정 (java의 final과 같다.)
  final constants = "this is a const";
  // constants = "nope"; // 컴파일 에러 발생

  // late는 초기화를 늦춰주는 역할을 한다. (값 초기화 이전에 발생하는 컴파일 에러를 방지해줌)
  // 아래 예시는 통신을 통해 상수로 활용할 값을 기다렸다가 통신 성공 시 지정해줄 때 이와 같이 활용할 수 있다.
  late final awaitResponseConst;
  awaitResponseConst = "i'm not modify";
  /** 이외의 다양한 케이스에 의한 late를 사용할 수도 있는데 이는 lateEx.dart 파일에 예시를 작성했다. */

  // const는 반드시 하드코디 되어야 한다. 즉, JS에서 처럼 사용자가 입력한 값을 const에 담아서 처리한다거나
  // api를 통해 받은 값을 const에 저장한다던가 하는 방식은 허용되지 않는다.
  const maxLength = 100;
  // 위처럼 반드시 내부적으로만 상수값으로 활용 될 때 사용하고
  // 통신을 통해 받거나, 유저 입력으로 받는 상수값인 경우는 final 또는 var를 활용해야 한다.

  /** ------------------------------  ㅡㅡㅡㅡㅡㅡ  -------------------------------- */

}

 

위 예제코드는 필자가 운영중인 Github에서 dart학습용

repository인 dart-intro에서 variable.dart 파일을

그대로 복붙했습니다.

Github: https://github.com/zeriong/dart-intro/blob/main/variables.dart

 

⭐유의사항

Dart언어에서 코드가 하나의 역할을 마치고 나서

세미콜론은 필수이다.

 

javascript에서 처럼 세미콜론을 생략한다고 auto formatter가

자동으로 추가 해주지 않고 dart에서 세미콜론 유무로

다른 기능으로 프로그래밍이 가능하기 때문에

필요에 의해 작성하거나 작성하지 않을 수 있기 때문이다.

 

✍️마치며...

이제야 Flutter개발자들이 말 끝마다 땀표(;)를 붙이는 이유를 알게 되었다... dart는 auto formatter가 세미콜론을 붙여주지 않기 때문에 반드시 세미콜론을 붙여야 한다. 이제부터라도 습관들여야겠다; 하하;

2024. 10. 5. 16:31

🎛️OverRoading

오버로딩이란 특정 클래스나 메서드를 작성할 때 내부에서

추가적으로 이름은 동일하게 작성하고 각기 다른 파라미터를

전달받아 파라미터에 따라서 각기 다른 역할을 수행할 수

있도록 구성하는 방식을 오버로딩(OverRoading)이라고 한다.

(오버라이딩과 오버로딩 착각에 유의하자...)

 

  • 예제코드
public class MemberConstruct {
    String name;
    int age;
    int grade;
    
    MemberConstruct(String name, int age) {
        this(name, age, 45);
        System.out.println("case 1 : grade를 넘겨받지 않은 생성자 메서드 호출");
        System.out.println("name : " + this.name + "\n");
    }

    MemberConstruct(String name, int age, int grade) {
        this.name = name;
        this.age = age;
        this.grade = grade;
        System.out.println("case 2 : grade를 전달받은 생성자 메서드 호출");
        System.out.println("name : " + this.name + "\n");
    }
}

위 예제는 클래스를 생성할 때 전달받는 파라미터에 따라서 다른 결과를 만들어낸다.

 

✍️마치며...

java를 배우면서 javascript에서는 신경쓰지 않아도 될 것들을 모두 지정하거나 추가해주어야 하는 것이 번거롭지만 오히려 좀 더 본질적인 프로그래밍에 가깝다는 느낌 또한 받게되는 것 같다.

 

이후 이어서 dart와 flutter도 공부를 천천히 시작하고 있는데 dart언어 또한 java와 뭔가 비슷하다는 느낌을 지울 수가 없었다. 아직도 모르는게 너무 많고 배울 것이 정말 많다... (오히려 좋아)

2024. 10. 3. 18:05

🐎OverRiding

오버라이딩이란 부모로부터 상속받은 메서드의 기능을

재정의하는 것처럼 보이는 현상을 의미한다

 

특정 클래스에 extends를 활용하여 부모 클래스를 상속한 경우

해당 클래스는 상속한 부모의 메서드를 사용 가능하게 되는데

 

이 때 상속받은 메서드의 기능이 아닌 다른 기능을 필요할 때

overriding을 활용하여 상속받은 메서드를 같은 이름으로

원하는 새로운 기능의 메서드를 생성하여 마치 같은 메서드이지만

재정의한 것 처럼 활용할 수 있다.

 

overriding이 적용되는 방식은 절차적인 접근순서에 의해 적용된다.

 

  • 상속 메서드 실행 예시코드
// Parent.java
public class Parent {

	public void walk() {
    		System.out.println("let's walk!");
    	}
}

// IHaveParent.java
public class IHaveParent extends Parent {

	public void wait() {
    		System.out.println("now wait for parent");
    	}
    
    	public void stay() {
    		System.out.println("and i stay here.");
    	}
}

// IHaveParentMain.java
// ...
IHaveParent iHaveParent = new IHaveParent();
iHaveParent.walk();

 

위 예시코드를 보면 Parent 클래스는 walk라는 메드를 가지고 있다.

 

이러한 경우 IHaveParent 인스턴스에서 walk를 호출하면

가장 먼저 IHaveParent클래스 자기 자신에서 부터 해당 메서드를

탐색하게 된다.

 

이 후 탐색할 수 없다면 상속된 부모클래스에서 walk 메서드를

탐색하게 되고 있다면 실행하게 된다.

 

  • Overriding 예시코드
// Parent.java
public class Parent {

	public void walk() {
    		System.out.println("let's walk!");
    	}
}

// IHaveParent.java
public class IHaveParent extends Parent {

		// 자식 요소에도 있는 walk 메서드
        public void walk() {
            	System.out.println("let's walk, alone.");
            }

	public void wait() {
    		System.out.println("now wait for parent");
    	}
    
    	public void stay() {
    		System.out.println("and i stay here.");
    	}
        

}

// IHaveParentMain.java
// ...
IHaveParent iHaveParent = new IHaveParent();
iHaveParent.walk();

 

위는 overriding이 적용된 예시 코드이다.

 

마찬가지로 특정 메서드를 찾기 위해 메서드를 탐색하고 이 때

"자기 자신이 가진 메서드를 먼저 탐색한다"는 절차적 순서로

호출된 메서드명을 가진 메서드가 부모 뿐만 아니라

자식 클래스에서도 가지고 있다면

 

자식클래스(자기 자신)에서 우선 탐색 후 실행되어 부모가 가진

메서드가 마치 재정의 되어 실행되는 것처럼 보이는 현상을

overriding이라고 한다.

 

서술한대로 탐색 순서에 의해 발생되는 현상이지만

JAVA 개발자들은 이를 의도하여 활용하고자 

Override 라는 어노테이션이 만들었다.

 

부모가 상속된 클래스 내부에서 부모가 가진

메서드명으로 다른 기능의 메서드를 만들고

상단에 Override어노테이션을 사용한다면

해당 메서드명이 부모에 존재한다면 정상 실행되지만

 

해당 메서드명이 존재하지 않는다면 에러를 발생시켜

상속관계를 가진 클래스에서 분명한 의도를 가진

프로그래밍 설계가 가능하도록 활용할 수 있다.

 

  • @Override를 활용한 예시
// Parent.java
public class Parent {

	public void walk() {
    		System.out.println("let's walk!");
    	}
}

// IHaveParent.java
public class IHaveParent extends Parent {

		// Override 어노테이션을 활용
        @Override
        public void walk() {
            	System.out.println("let's walk, alone.");
            }

	public void wait() {
    		System.out.println("now wait for parent");
    	}
    
    	public void stay() {
    		System.out.println("and i stay here.");
    	}
        

}

// IHaveParentMain.java
// ...
IHaveParent iHaveParent = new IHaveParent();
iHaveParent.walk();

 

 

✍️마치며...

자바를 공부하며 새로운 프로그래밍 개념에 대해서 알아가며 새삼 프로그래밍 자체가 가진 프로세스, 로직은 얼추 비슷하게 적용되고 있음을 깨닫게 되는 것 같다. 특히나 자바와 (타입스크립트 + JS)는  무척이나 유사한 것 같다... 자바를 공부하며 기존에 다루었던 NestJS와도 많이 닮았다는 생각을 했는데 결과적으로는 타입스크립트가 자바를 이해하는데 크게 기여한 것을 느낄 수 있었다. 앞으로도 배워보고 싶은 언어, 프레임워크가 많은 만큼 더 열심히 해야겠다!

2024. 9. 28. 21:42

📚Convention

컨벤션은 말 그대로 예로부터 내려오는 일명 "국룰"이다.

convention을 직역하면 "전통적인"이라는 뜻으로

효율적으로 아키텍처를 구성할 수 있도록 인간의 사고력을

기준으로 누구나 이해하기 쉽도록 예로부터 내려오는

개발자들의 네이밍 방식, 폴더구조, 모듈화 방식 등을 의미한다.

 

🤔Why do we need conventions?

우리에게 컨벤션이 필요한 이유로 가장 먼저 떠오르는 것은

협업을 위한 컨벤션일 것이다.

 

팀과함께 프로젝트를 진행함에 있어 일관성을 만들어

하나의 약속으로서 팀원 간에 불필요한 커뮤니케이션을

최소화하기 위해서 컨벤션을 만드는 것이 일반적으로

떠올릴 수 있는 필요성으로 생각이 든다.

 

그렇다면 협업을 하지않는다면 컨벤션이 필요없는가?

 

그렇지않고 생각한다. 결과적으로 일관성을 지키는 것은 전체적인

아키텍처를 구성함에 있어 작업 효율성과 가독성을 증대시키는

효과를 기대할 수 있다.

 

혼자 프로젝트를 구성하여 서비스를 만든다고 하여도

개발자 커뮤니티에서 전통적으로 활용되는 아키텍처 컨벤션을

이해하며, 납득 가능한 근거를 기반으로 자기만의 컨벤션(기준)을

세워 꾸준히 일관성을 만들어간다면 기본적으로 설계가 빨라지고

유지보수면에서도 큰 이점을 가져올 수 있다.

 

🐈‍⬛git-commit-message-conventions

깃허브에 커밋 메시지를 남길 때 또한 컨벤션이 존재한다.

기본적으로 head, body, footer로 구분하여 활용하는데

너무 길어도 좋지 않기 때문에 일반적으로 head만 활용하여

간단명료하게 활용하는 것을 개발자들이 보편적으로

선호하는 방식인 듯 하다.

 

필자 역시 프로젝트를 만들어 진행할 때 head와 함께

간략하게 이전 코드에서 변경사항을 커밋메시지에 기록하는

방식으로 컨벤션을 꾸준히 지키고있다.

 

  • 커밋 메시지 예시
feat: 알림기능 추가
refactor: 글쓰기 기능 리렌더링 최적화
design: 변경된 디자인 시안 적용

 

⭐자주 쓰이는 commit message head

  • feat: 새로운 기능 추가
  • fix: 버그 수정 또는 typo
  • refactor: 리팩토링
  • design: CSS 등 사용자 UI 디자인 변경
  • comment: 필요한 주석 추가 및 변경
  • style: 코드 포맷팅, 세미콜론 누락, 코드 변경이 없는 경우
  • test: 테스트(테스트 코드 추가, 수정, 삭제, 비즈니스 로직에 변경이 없는 경우)
  • Chore: 위에 걸리지 않는 기타 변경사항(빌드 스크립트 수정, assets image, 패키지 매니저 등)
  • init: 프로젝트 초기 생성
  • rename: 파일 혹은 폴더명 수정하거나 옮기는 경우
  • remove: 파일을 삭제하는 작업만 수행하는 경우

 

✍️마치며...

개발자 취준생 초반에는 컨벤션이라는 개념이 없었지만 개인 프로젝트를 진행하며 기능이 많아지고 폴더 아키텍처가 복잡해질수록 프로그래밍을할 때 기준이 필요하다는 것을 깨닫게 되었다.

 

프로그래밍에도 컨벤션이 있다는 것을 알게되었고 어째서 컨벤션이 이러한 형식으로 이어져왔는지에 대한 공부를 하게 되었고, 이 후 클린코드에 대해서도 알게 되어 코드를 거듭 리팩토링하며 나만의 방식을 찾을 수 있게된 것 같다.

 

지금은 개발자로 취업하고서 협업을 하면서 컨벤션의 중요성을 뼈저리게 느끼며 팀원 모두가 납득할 수 있는 컨벤션을 제시하고 서로 맞춰가는 것 또한 개발자로서의 중요한 역량 중 하나라는 것을 느끼게 되었다.

2024. 9. 28. 20:17

⌨️Command

git commit --amend

 

  1. 위 명령어를 터미널에서 입력 후 i 를 입력하여 insert모드로 진입 후 메시지를 변경한다.
  2. esc 누르고 :wq 입력하여 설정모드에서 escape한다.
  3. 깔끔하게 적용할 땐 rebase를, PR기록을 남기고자 하는 경우는 merge를 한다.

'Git > Git Commands' 카테고리의 다른 글

[ Git-commands ] Git 변경사항 임시저장하기  (2) 2024.03.31