-
-
-
-
JAVA
자바 (인테리제이 초반 설정)
인텔리제이 설정
플러그인(Plugins)
CheckStyle-IDEA
> JAVA로 하는 ESLINT 같은 느낌
>
setting에서 CheckStyle설정하기
naver checks를 다운 받아서 CheckStyle에서 +버튼으로 rule을 등록한다
그리고 같이 다운 받은 xml파일을 value에 파일명으로 넣어준다
https://github.com/naver/hackday-conventions-java/blob/master/rule-config/naver-checkstyle-rules.xml
https://github.com/naver/hackday-conventions-java/blob/master/rule-config/naver-checkstyle-suppressions.xml
>
Code style도 들어가서 java에서 설정할것 Scheme에서 받은 네이버 파일 열어서 쓰기
https://github.com/naver/hackday-conventions-java/blob/master/rule-config/naver-intellij-formatter.xml
다운받은파일
그리고 action들어가서 세이브 할때마다 프리티어 적용되게 설정하기
optimize imports 체크
Reformat code 체크
system settings들어가서 자동 저장 설정
Atom Material Icons
> 깔끔한 아이콘 보기
Key promoter X
>
VSCode Keymap
>
Material Theme
> 테마 설정가능
터미널에서 자바실행하는 방법
해당 자바파일이 있는 곳에서
javac 파일이름.java
하면 파일이름.class가 생긴다
여기서 이제
java 파일이름.java
하면 터미널에서 실행이 된다
package com.hana.greetings;
import java.util.Scanner;
public class Hi {
public static void main(String[] args) {
scanTemp();
>>메인에다가 이렇게 쓰고 알트+엔터로 크리에이트메소드로 뺀다!
}
>>그러면 아래처럼 생긴다!
private static void scanTemp() {
Scanner scan = new Scanner(System.in);
System.out.println();
}
}
자바 args[]에 값을 넣고 실행하기
javac 이름.java
java 패키지.이름 args[]
으로arg[]값을 넣어서 실행가능
또는 인텔리제이의 윗창에서
current file의 오른쪽에있는 ...(세로)를
눌러서 거기에 값을 넣고 실행을 해주면 된다
접근 제어자
public - 모든 클래스에서 접근 가능
protected - 같은 패키지 또는 자식 클래스에서 접근 가능
private - 같은 클래스 내에서만 접근 가능
제너릭
public class Box<T> {
private T item;
public void setItem(T item) {
this.item = item;
}
}
자바의 제너릭(Generic)은 코드의 재사용성을 높이기 위해 특정 데이터 타입을 미리 정의하지 않고, 런타임에 사용할 수 있도록 만든 기능입니다. 제너릭을 사용하면 코드의 안정성을 높이면서 타입 변환을 줄일 수 있어 컴파일 시 오류를 줄일 수 있습니다.
여기서 는 타입 파라미터를 의미하며, 다양한 타입을 하나의 클래스, 메서드, 인터페이스에서 사용할 수 있도록 합니다.
제너릭 안에서는 static으로 T를 선언하면 오류를
제너릭 에서 static T는 컴파일 오류가 뜬다. why? static 멤버는 클래스 로딩 시점에 메모리에 로드되기 때문에, 제네릭 타입 파라미터 T와 같이 인스턴스에 따라 달라지는 타입 정보를 가질 수 없습니다.
<? super T>: 하위 타입 한정 와일드카드 - 특정 타입 T와 그 상위 타입만 허용, 주로 데이터를 넣을 때 사용됨
<?>: 비한정 와일드카드 - 비한정 와일드카드로 모든 타입을 허용, 주로 읽기 전용으로 사용할 때 유용
<? extends T>: 상위 타입 한정 와일드카드 - 특정 타입 T와 그 하위 타입만 허용, 주로 데이터를 읽기 전용으로 사용할 때 사용됨
도 있다
StringBuffer
자바에서 문자열을 효율적으로 수정할 수 있도록 만들어진 클래스입니다. String은 불변(immutable) 클래스라서 한 번 생성되면 내용이 바뀌지 않지만, StringBuffer는 가변(mutable) 클래스이기 때문에 문자열을 수정, 추가, 삭제하는 작업에 유리합니다.
주요 메서드
append(): 문자열을 뒤에 추가
insert(): 특정 위치에 문자열 삽입
delete(): 특정 범위의 문자열 삭제
reverse(): 문자열을 뒤집음
toString(): StringBuffer 객체를 String으로 변환
```javapublic class Main {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer(“Hello”);
sb.append(" World"); // "Hello World"로 수정
System.out.println(sb); // 출력: Hello World
sb.insert(6, "Java "); // "Hello Java World"로 수정
System.out.println(sb); // 출력: Hello Java World
sb.delete(6, 11); // "Hello World"로 수정
System.out.println(sb); // 출력: Hello World
sb.reverse(); // "dlroW olleH"로 수정
System.out.println(sb); // 출력: dlroW olleH
} }
```
콜렉션
-
-
NEXT
NEXT
https://nextjs.org/learn
NEXT 14를 사용하자
리액트 베이스의 풀스택이다!
생성
pnpm dlx create-next-app@latest
What is your project named? my-app
Would you like to use TypeScript? No / Yes
Would you like to use ESLint? No / Yes
Would you like to use Tailwind CSS? No / Yes
Would you like to use src/ directory? No / Yes
Would you like to use App Router? (recommended) No / Yes
Would you like to customize the default import alias (@/)? No / Yes
What import alias would you like configured? @/
prettier & eslint-plugin-react
pnpm add -D prettier eslint-config-prettier
pnpm add -D eslint-plugin-react eslint-plugin-react-hooks @next/eslint-plugin-next
.eslintrc.json
{
"extends": ["next/core-web-vitals", "prettier", "plugin:react/recommended", "plugin:react/jsx-runtime", "plugin:react-hooks/recommended"]
}
pnpm add -D @trivago/prettier-plugin-sort-imports
.prettierrc
{
"singleQuote": true,
"jsxSingleQuote": true,
"semi": true,
"trailingComma": "es5",
"plugins": ["@trivago/prettier-plugin-sort-imports"],
"importOrder": [
"^next$",
"^next/\\w*$",
"^next/(.*)$",
"^react$",
"^react/(.*)$",
"^@/lib/(.*)$",
"^[./]"
]
}
프리티어 테스트
$> pnpm format
>> 오류 뜨면
pnpm format --write
>> 로 설정잡기
빌드(스타트)
pnpm build
>>빌드먼저 해주고
pnpm start
next-auth v5
$> pnpm add next-auth@beta
>> 설치
$> pnpm dlx auth secret
>> 키 생성 (.env.local)
-
NEXT
NEXT
https://nextjs.org/learn
NEXT 14를 사용하자
리액트 베이스의 풀스택이다!
폴더가 라우터다
(폴더이름)내 맘대로 만들고
그안에 들어가는 구성요소는 page, layout, template 이름 고정이다
ex) about 폴더
1. mkdir app/about (Route Folder)
2. make app/about/layout.tsx
3. make app/about/page.tsx
4. nav to page from Home(app/page.tsx)
서버에서 이루어진다.
위쪽에 “use client”는 Next.js 13 이상에서 도입된 기능으로, 컴포넌트가 클라이언트 컴포넌트임을 명시하는 선언입니다.
프리티어(위에 임포트 순서 설정)
.prettierrc
{
"singleQuote": true,
"jsxSingleQuote": true,
"semi": true,
"trailingComma": "es5",
"plugins": ["@trivago/prettier-plugin-sort-imports"],
"importOrder": [
"^next$",
"^next/\\w*$",
"^next/(.*)$",
"^react$",
"^react/(.*)$",
"^@/lib/(.*)$",
"^[./]"
]
}
>에서 importOrder의 내부 순서를 바꾸어주면 import되는것들의 순서를 지정해줄 수 있다.
yarn dev
yarn build -> yarn start
차이를 알아두기(dev에서는 계속 시간같은거 업데이트 가능하지만, start에서는 배포용이라서 딱 그 시점?이 올라감, 따라서 다이나믹하게 만들어줘야함)
useRouter (only Client Component)
'use client'; > 이거는 이제 클라이언트(=브라우저)에서 실행이 되게끔한다!!!
//클라이언트 컴포넌트에서는 어싱크(async)를 못 쓴다.
// useRouter
import { useRouter } from 'next/navigation'
const router = useRouter()
router.push('/dashboard', { scroll: false });
// another functions
router.back();
useSearchParam
??
다이나믹하게 페이지
// /hi/[time]/page.tsx
export default function HiTime({ params }: { params: { time: string } }) {
return <div>Good {params.time}!</div>
}
()는 경로에 영향을 미치지 않음(정리용)
/app
/(auth)
/login/page.tsx
/dashboard/page.tsx
여기서 (auth)는 그냥 더 가독성 좋게 보일려고하는거임
인터셉트
client router와 server router를 다르게 표현
next/link/Link를 이용해야 intercepting 됨.
A → B(intercept) → B(refresh)
(.) 같은 레벨/depth
(..) 하나 위 레벨
(...) Root 레벨
(..)(..) 두개 위 레벨
원래 원본의 파일이 있는데 그 파일로 이동하는 것이 아닌 내가 만든 인터셉트 페이지로 이동을 시킨다(이것은 새로고침하면 원본페이지로 이동이 된다),
(.)/ (..) / (…) 같은경우는 그냥 폴더 위치에 따른 경로 느낌
[[]] 대괄호 두개
[[...params]]와 같은 형태는 Next.js에서 Catch-All 라우팅을 사용할 때, 대괄호를 2개 사용하는 방식입니다. 이것은 동적 경로를 정의할 때 매우 유용합니다. 대괄호 두 개([[...]])는 특정 경로가 **선택적 동적 경로(선택적 Catch-All)**임을 의미합니다.
1. [...params] (Catch-All 라우팅):
설명: [...]를 사용하면 URL의 나머지 부분을 하나의 배열로 캡처할 수 있습니다.
예시: /pages/blog/[...slug].tsx
이 경우, blog 경로의 하위 모든 경로가 slug 배열로 캡처됩니다.
예를 들어:
/blog/hello/world → { slug: ['hello', 'world'] }
/blog/my-post → { slug: ['my-post'] }
2. [[...params]] (선택적 Catch-All 라우팅):
설명: 대괄호 두 개([[...]])를 사용하면 선택적 동적 경로가 됩니다. 즉, 이 경로는 있어도 되고 없어도 되는 선택적 경로입니다.
예시: /pages/blog/[[...slug]].tsx
이 경우, blog 경로는 동적 경로가 없어도 매칭이 됩니다.
예를 들어:
/blog → { slug: [] } (빈 배열)
/blog/hello/world → { slug: ['hello', 'world'] }
/blog/my-post → { slug: ['my-post'] }
요약:
[...params]: Catch-All 동적 경로로서, 지정한 경로 이후의 모든 부분을 배열로 캡처합니다.
[[...params]]: 선택적 Catch-All 동적 경로로서, 경로가 있어도 되고 없어도 됩니다. 경로가 없을 경우 빈 배열로 반환됩니다.
이 패턴을 통해 유연한 라우팅을 구성할 수 있으며, 다양한 경로 변형을 처리하는 데 매우 유용합니다.
use client 와 asyncs는 같이 사용을 못한다.
"use client"와 async 함수는 각자 다른 환경에서 동작하기 때문에, 서버 컴포넌트와 클라이언트 컴포넌트의 특성 차이로 인해 제약이 발생합니다.
> 왜 use client와 async를 함께 사용할 수 없을까?
클라이언트 컴포넌트는 동기적으로 렌더링:
Next.js에서 클라이언트 컴포넌트는 브라우저에서 동기적으로 렌더링됩니다. 클라이언트 컴포넌트는 동기적인 렌더링 흐름을 가져야 하기 때문에 컴포넌트 자체가 async 함수가 될 수 없습니다.
만약 컴포넌트 전체가 async로 선언되면, 해당 컴포넌트가 반환할 때까지 기다려야 하는데, 이는 브라우저의 동기 렌더링 흐름과 맞지 않기 때문에 에러가 발생합니다.
데이터 가져오기와 렌더링의 분리:
클라이언트 컴포넌트에서는 데이터 가져오기를 위해 React hooks를 사용해야 합니다. useEffect, useState 같은 React hooks로 비동기 작업을 처리하고, 상태 업데이트를 통해 다시 렌더링해야 합니다.
서버 컴포넌트에서는 서버에서 데이터를 미리 가져온 후 그 결과를 클라이언트로 넘겨주지만, 클라이언트 컴포넌트는 브라우저에서 비동기 작업을 별도로 처리하고 렌더링 흐름을 제어해야 합니다.
Image를 사용하기 위한 설정
// for Image component in next.config.mjs
/** @type {import('next').NextConfig} */
const nextConfig = {
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'via.placeholder.com',
port: '',
pathname: '/**',
},
],
},
};
export default nextConfig;
Next Cloudinary Image(사진) 편집???
https://next.cloudinary.dev/
shadcn 설치!!!
https://ui.shadcn.com/docs/installation/next
pnpm dlx shadcn@latest init
아이콘 패키지 설치!!
https://heroicons.com/
$> pnpm add @heroicons/react
Next-auth 설치
$> pnpm add next-auth@beta
$> pnpm dlx auth secret
>> .env.local의 AUTH_SECRET 자동 세팅!
-
NEXT
NEXT
https://nextjs.org/learn
NEXT 14를 사용하자
리액트 베이스의 풀스택이다!
Next.js에서 SSG, SSR, ISR, CSR에 대해 한국어로 설명드리면:
SSG (정적 사이트 생성):
설명: SSG는 페이지를 빌드 시점에 미리 렌더링하여 정적인 HTML 파일로 생성합니다. 이렇게 생성된 페이지는 이후 요청 시 서버에서 그대로 제공됩니다.
Next.js에서의 동작 방식: getStaticProps와 비슷하며, 이 메서드를 사용해 빌드 시 데이터를 가져와 페이지를 생성합니다.
코드 예시:
js
코드 복사
const staticData = await fetch('https://...', { cache: 'force-cache' });
force-cache는 데이터를 빌드 시에만 캐싱하고 이후로는 그대로 제공하는 방식입니다.
SSR (서버 사이드 렌더링):
설명: SSR은 매 요청 시 서버에서 페이지를 렌더링하고 HTML을 클라이언트로 전달합니다. 페이지는 사용자 요청마다 새롭게 렌더링되므로, 최신 데이터를 항상 반영합니다.
Next.js에서의 동작 방식: getServerSideProps와 유사하며, cache: 'no-store'를 사용하여 서버가 캐싱 없이 매번 새로운 데이터를 받아옵니다.
코드 예시:
js
코드 복사
const dynamicData = await fetch('https://...', { cache: 'no-store' });
ISR (증분 정적 재생성):
설명: ISR은 정적 사이트 생성(SSG)와 유사하지만, 지정된 시간 간격으로 페이지를 재생성합니다. 즉, 페이지가 처음에는 정적으로 생성되지만, 설정된 시간(예: 10초)이 지나면 새 데이터를 반영해 페이지를 다시 생성할 수 있습니다.
Next.js에서의 동작 방식: getStaticProps에서 revalidate 옵션을 사용하는 방식과 유사하며, 이를 통해 주기적으로 데이터를 다시 가져옵니다.
코드 예시:
js
코드 복사
const revalidatedData = await fetch('https://...', { next: { revalidate: 10 } });
이 예시는 10초마다 데이터를 다시 가져와 페이지를 재생성합니다.
CSR (클라이언트 사이드 렌더링):
설명: CSR은 클라이언트에서 모든 페이지를 렌더링하는 방식입니다. 즉, JavaScript가 로드된 후 브라우저에서 데이터를 가져오고 페이지를 렌더링합니다. React의 SPA(단일 페이지 애플리케이션)와 비슷한 방식입니다.
Next.js에서의 동작 방식: 페이지 컴포넌트에 'use client';를 선언하면 해당 페이지는 클라이언트 측에서만 렌더링됩니다.
코드 예시:
js
코드 복사
'use client';
이렇게 각 렌더링 방식은 페이지의 데이터 처리 방식과 성능에 따라 다르게 적용될 수 있습니다.
pnpm
npm i pnpm -g
>로 설치
pnpm dlx create-next-app@latest
>로 생성한다.
pnpm build
>로 빌드
pnpm start
>페이지열기
프리티어 설정
// 프리티어
pnpm add -D prettier eslint-config-prettier
// package.json > scripts
"format": "prettier --check --ignore-path .gitignore .",
"format:fix": "prettier --write --ignore-path .gitignore ."
.eslintrc.json bold 부분 추가!
{
"extends": ["next/core-web-vitals", "prettier", "plugin:react/recommended", "plugin:react-hooks/recommended", "plugin:react/jsx-runtime"]
}
//.prettierrc 파일 생성!
{
"singleQuote": true,
"jsxSingleQuote": true,
"semi": true,
"tabWidth": 2,
"trailingComma": "es5"
}
> 프리티어 자동으로 단축키 -> 쉬프트 + 알트 + F
프리티어 ++++
install
$> pnpm add -D @trivago/prettier-plugin-sort-imports
.prettierrc 파일 생성!
{
"singleQuote": true,
"jsxSingleQuote": true,
"semi": true,
"trailingComma": "es5",
"plugins": ["@trivago/prettier-plugin-sort-imports"],
"importOrder": [
"^next$",
"^next/\\w*$",
"^next/(.*)$",
"^react$",
"^react/(.*)$",
"^@/lib/(.*)$",
"^[./]"
]
}
그리고
pnpm format
> 실행
eslint
pnpm add -D eslint-plugin-react eslint-plugin-react-hooks @next/eslint-plugin-next
-
React
리액트
##
프론트 면접
use디바운스,debounce가 너무 중요하다!!
훅들에 대해서 프론트엔드 기술 면접을 물어본다!! 어떻게 동작이 되는지 알아두기
아침정리
useState
useState는 컴포넌트의 상태(state)를 관리하는 훅입니다. 상태는 컴포넌트의 값이나 UI를 동적으로 변경할 때 사용됩니다.
import React, { useState } from 'react';
function Counter() {
// count는 상태 값, setCount는 상태를 업데이트하는 함수
const [count, setCount] = useState(0);
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
useState(0)은 count의 초기값을 0으로 설정하고, setCount 함수로 count 값을 변경할 수 있습니다.
버튼을 클릭하면 상태가 증가하고, 컴포넌트가 다시 렌더링됩니다.
useRef
useRef는 DOM 요소에 접근하거나 렌더링 간 유지해야 하는 값을 저장하는 데 사용됩니다. 렌더링과 상관없이 유지되는 값을 저장하며, 렌더링을 트리거하지 않습니다.
import React, { useRef } from 'react';
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onFocusClick = () => {
// input 요소에 직접 접근해 포커스를 줌
inputEl.current.focus();
};
return (
<div>
<input ref={inputEl} type="text" />
<button onClick={onFocusClick}>Focus the input</button>
</div>
);
}
useRef를 통해 inputEl.current로 DOM 요소에 접근할 수 있습니다.
onFocusClick을 통해 버튼을 클릭하면 입력 필드에 포커스를 줍니다.
useContext
useContext는 React의 Context API에서 전역 상태나 데이터를 소비할 때 사용합니다. Context를 통해 컴포넌트 트리에서 값을 props로 전달하지 않고 사용할 수 있습니다.
import React, { createContext, useContext, useState } from 'react';
const UserContext = createContext(null);
function App() {
const [user, setUser] = useState({ name: 'John', age: 30 });
return (
<UserContext.Provider value={user}>
<Profile />
</UserContext.Provider>
);
}
function Profile() {
const user = useContext(UserContext);
return <div>{`Hello, ${user.name}!`}</div>;
}
UserContext.Provider에서 user 데이터를 하위 컴포넌트에 전달하고, Profile 컴포넌트에서 useContext(UserContext)를 사용해 user 값을 가져옵니다.
useEffect
useEffect는 컴포넌트가 렌더링되거나 업데이트될 때 부수 효과를 처리하기 위해 사용됩니다. 예를 들어, 데이터 패칭, 이벤트 리스너 등록/제거, 타이머 설정 등을 처리할 때 유용합니다.
import React, { useState, useEffect } from 'react';
function Timer() {
const [count, setCount] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setCount((prevCount) => prevCount + 1);
}, 1000);
// 컴포넌트가 언마운트될 때 타이머를 정리함
return () => clearInterval(timer);
}, []);
return <div>{count}</div>;
}
useEffect는 컴포넌트가 렌더링될 때 타이머를 시작하고, 컴포넌트가 언마운트되면 타이머를 정리합니다.
타이머는 1초마다 count 값을 증가시킵니다.
useLayoutEffect
useLayoutEffect는 DOM이 업데이트된 후 바로 실행되며, 화면에 그려지기 전에 동기적으로 실행됩니다. DOM 조작이 필요할 때 사용됩니다.
import React, { useState, useLayoutEffect, useRef } from 'react';
function LayoutEffectExample() {
const [size, setSize] = useState(0);
const divRef = useRef(null);
useLayoutEffect(() => {
// div의 너비를 가져와서 상태로 저장
if (divRef.current) {
setSize(divRef.current.offsetWidth);
}
});
return (
<div>
<div ref={divRef} style={{ width: '100px', height: '100px', backgroundColor: 'lightblue' }} />
<p>Div width: {size}px</p>
</div>
);
}
useLayoutEffect는 DOM이 업데이트된 후 동기적으로 실행되며, div의 크기를 계산해 size 상태로 저장합니다.
useCallback
useCallback은 특정 함수의 재생성을 방지하는 훅입니다. 컴포넌트가 렌더링될 때마다 불필요하게 함수가 다시 생성되는 것을 막고, 성능 최적화에 도움을 줍니다.
import React, { useState, useCallback } from 'react';
function Counter() {
const [count, setCount] = useState(0);
const increment = useCallback(() => {
setCount((prevCount) => prevCount + 1);
}, []);
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
useCallback을 사용해 increment 함수가 재생성되지 않도록 설정했습니다. 이는 컴포넌트가 불필요하게 렌더링되지 않도록 방지합니다.
useMemo
useMemo는 특정 값의 재계산을 방지하고, 메모이제이션을 통해 성능 최적화를 수행합니다. 컴포넌트가 렌더링될 때마다 값이 다시 계산되지 않도록 제어할 수 있습니다.
import React, { useState, useMemo } from 'react';
function ExpensiveCalculation(num) {
console.log('Expensive calculation...');
return num * 2;
}
function Calculator() {
const [number, setNumber] = useState(0);
const [text, setText] = useState('');
const doubledNumber = useMemo(() => ExpensiveCalculation(number), [number]);
return (
<div>
<input value={text} onChange={(e) => setText(e.target.value)} placeholder="Type something" />
<button onClick={() => setNumber(number + 1)}>Increment Number</button>
<p>Doubled Number: {doubledNumber}</p>
</div>
);
}
useMemo를 사용해 ExpensiveCalculation 함수가 number 값이 변경될 때만 다시 실행되도록 합니다. 이를 통해 불필요한 재계산을 방지합니다.
테일윈드
$ yarn add tailwind-merge
https://www.npmjs.com/package/tailwind-merge
clsx
yarn add clsx
라우팅
yarn add react-router-dom
yarn add -D @types/react-router-dom
-
-
Java 클래스
Java
자바 클래스 핵심
class Student {
int kor, eng, math;
public Student(int kor, int eng, int math){
this.kor = kor;
this.eng = eng;
this.math = math;
} // 생성자(constructor) 라 불리는 함수
};
// 초기값을 주고 싶을때는 동일한 것을 선언하여서 주면된다
class Student {
int kor, eng, math;
public Student(){
this.kor = 0;
this.eng = 0;
this.math = 0;
}
public Student(int kor, int eng, int math){
this.kor = kor;
this.eng = eng;
this.math = math;
}
};
public class Main {
public static void main(String[] args) {
Student student1 = new Student(90, 80, 90); // 넘어간 값을 사용
System.out.println(student1.kor); // 90
System.out.println(student1.eng); // 80
System.out.println(student1.math); // 90
Student student2 = new Student(); // 값이 넘어가지 않는 생성자를 이용
System.out.println(student2.kor); // 0
System.out.println(student2.eng); // 0
System.out.println(student2.math); // 0
}
}
객체 정렬 @override로 / implements Comparable
Java에서는 custom comparator를 만들어야 합니다. 이 함수는 반환 타입이 꼭 int 이어야 하며, 해당하는 class를 type으로 하는 1개의 인자를 갖고 있어야만 합니다. 가장 일반적인 방법은 class 뒤에 implements Comparable를 붙이고 public int compareTo 함수를 class 안에 override annotator와 함께 적어주시면 됩니다.
class Student implements Comparable<Student> {
int kor, eng, math;
public Student(int kor, int eng, int math){
this.kor = kor;
this.eng = eng;
this.math = math;
}
@Override
public int compareTo(Student student) { // 국어 점수 기준 오름차순 정렬
if(this.kor > student.kor)
return 1;
else if(this.kor < student.kor)
return -1;
else
return 0;
}
};
///
@Override
public int compareTo(Student student) { // 국어 점수 기준 오름차순 정렬
return this.kor - student.kor;
}
하지만 Java 8 부터는 굳이 정렬 기준을 class안에 적지 않고, Arrays.sort 사용시 인자로 정렬 함수를 다음과 같이 넣어주는 방식도 가능합니다. 두 인자 값을 순서대로 a, b라 했을 때 국어 점수 기준 오름차순 정렬을 위해서는 a.kor - b.kor를 반환해주면 됩니다. 이는 lambda라는 표현을 사용하여 함수를 간단히 작성이 가능합니다.
Arrays.sort(students, (a, b) -> a.kor - b.kor); // 국어 점수 기준 오름차순 정렬
comparator를 사용한 정렬
// custom comparator를 활용한 정렬
Arrays.sort(students, new Comparator<Student>() {
@Override
public int compare(Student a, Student b) { // 키를 기준 오름차순 정렬합니다.
return a.height - b.height;
}
});
comparator를 이용해서 main안에서 정렬 때리기
import java.util.Scanner;
import java.util.Arrays;
import java.util.Comparator;
// 학생들의 정보를 나타내는 클래스 선언
class Student {
String name;
int height;
double weight;
public Student(String name, int height, double weight){
this.name = name;
this.height = height;
this.weight = weight;
}
};
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 변수 선언 및 입력:
int n = 5;
Student[] students = new Student[n];
for(int i = 0; i < n; i++) {
String name = sc.next();
int height = sc.nextInt();
double weight = sc.nextDouble();
// Student 객체를 생성해 리스트에 추가합니다.
students[i] = new Student(name, height, weight);
}
// custom comparator를 활용한 정렬
Arrays.sort(students, new Comparator<Student>() {
@Override
public int compare(Student a, Student b) { // 이름 기준 오름차순 정렬합니다.
return a.name.compareTo(b.name);
}
});
// 이름순으로 정렬한 결과를 출력합니다.
System.out.println("name");
for (int i = 0; i < n; i++){
System.out.print(students[i].name + " ");
System.out.print(students[i].height + " ");
System.out.printf("%.1f\n", students[i].weight);
}
System.out.println();
// custom comparator를 활용한 정렬
Arrays.sort(students, new Comparator<Student>() {
@Override
public int compare(Student a, Student b) { // 키 기준 내림차순 정렬합니다.
return b.height - a.height;
}
});
// 키순으로 정렬한 결과를 출력합니다.
System.out.println("height");
for (int i = 0; i < n; i++){
System.out.print(students[i].name + " ");
System.out.print(students[i].height + " ");
System.out.printf("%.1f\n", students[i].weight);
}
}
}
-
-
Java 함수
Java
자바 함수 핵심
public static final int MAX_N = 100;
//
//final (Java):
final은 값을 변경할 수 없는 변수를 선언할 때 사용합니다.
기본 데이터 타입(예: int, double)에서는 값 자체가 고정됩니다.
참조 데이터 타입(예: 객체, 배열)에서는 **참조(주소)**가 고정되지만, 객체의 내부 상태는 변경할 수 있습니다.
클래스나 메서드에도 사용할 수 있어 상속을 금지하거나 오버라이딩을 방지할 수 있습니다.
//
//const (JavaScript):
const는 재할당이 불가능한 변수를 선언할 때 사용합니다.
기본 데이터 타입에서는 값 자체가 고정됩니다.
객체나 배열 같은 참조 데이터 타입에서는 **참조(주소)**가 고정되지만, 내부의 값을 변경할 수 있습니다.
const는 **블록 범위(block scope)**를 가지며, 선언된 블록을 벗어나면 유효하지 않습니다.
//
콜 바이 래퍼런스
즉, Java에서의 swap은 Reference type을 이용하여 다음과 같이 구현해야만 합니다
class IntWrapper {
int value;
public IntWrapper(int value) {
this.value = value;
}
}
Java에서 생성자는 반드시 클래스와 같은 이름을 가져야 합니다. 그리고 접근 제어자(access modifier)는 선택 사항입니다. 생성자의 접근 제어자는 다음과 같이 설정할 수 있습니다:
public: 클래스 외부에서도 생성자를 호출할 수 있습니다. 가장 일반적인 접근 제어자입니다.
private: 클래스 내부에서만 생성자를 호출할 수 있습니다. 일반적으로 싱글턴 패턴(Singleton pattern)이나 정적 메서드만을 통해 객체 생성을 허용할 때 사용됩니다.
protected: 같은 패키지에 있는 다른 클래스나 해당 클래스를 상속받은 서브 클래스에서 생성자를 호출할 수 있습니다.
default (아무 접근 제어자도 지정하지 않음): 같은 패키지 내의 클래스에서만 접근할 수 있습니다.
public class Main {
public static void swap(IntWrapper n, IntWrapper m) {
int temp = n.value;
n.value = m.value;
m.value = temp;
}
public static void main(String[] args) {
IntWrapper n = new IntWrapper(10);
IntWrapper m = new IntWrapper(20);
swap(n, m);
System.out.println(n.value + " " + m.value); // 20 10
}
}
clone , 배열
가장 간단한 방법으로, 함수로 값을 넘길때 다음과 같이 .clone()을 하게 되면 기존 배열과 동일한 원소를 갖는 새로운 배열을 만들어 넘겨주게 되므로, 함수 안에서의 변화가 함수 밖의 변수에 영향을 끼치지 않게 됩니다.
public class Main {
public static void modify(int[] arr2) { // arr2는 arr와 관련이 없다.
arr2[0] = 10;
}
public static void main(String[] args) {
int[] arr = new int[]{1, 2, 3, 4};
modify(arr.clone()); // 새로운 배열을 만들어 넘기기
for(int i = 0; i < 4; i++)
System.out.print(arr[i] + " ");
}
}
>> 1 2 3 4 # 값에 변화가 없다
import java.util.Scanner;
public class Main {
public static final int MAX_N = 50;
public static int n;
public static int[] arr = new int[MAX_N];
// 위에 처럼 선언을 main밖에 해줘서 써죽
public static void modify(int[] arr) {
for(int i = 0; i < n; i++)
if(arr[i] % 2 == 0)
arr[i] /= 2;
}
public static void main(String[] args) {
// 변수 선언 및 입력:
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
for(int i = 0; i < n; i++)
arr[i] = sc.nextInt();
modify(arr);
for(int i = 0; i < n; i++)
System.out.print(arr[i] + " ");
}
}
java는 String도 함수로 바로 연결못함
함수에서 해당 인자 값을 변경하였지만, 함수 밖에 있는 변수에는 영향을 끼치지 않는 것을 확인할 수 있습니다.
이는 String은 immutable하기 때문입니다. immutable하다는 것은 값을 바꿀 수 없는 type이라는 뜻입니다.
값을 바꿀 수 없으니 s += “apple”이라는 구문은 기존 s 문자열 뒤에 apple을 붙이는게 아닌, 실제 s + “apple” 를 계산하여 새로운 문자열을 만든 뒤 이 값을 s에 대입하게 됩니다.
동시에 String은 reference type입니다. 즉, 함수 안에서 변수 s에 새로운 값을 대입시켜봤자, main함수에서 정의된 “banana”라는 값에는 전혀 영향을 끼칠 수가 없는 것입니다.
public class Main {
public static void modify(String s) {
s += "apple";
}
public static void main(String[] args) {
String str = "banana";
modify(str);
System.out.print(str);
}
}
>>> banana
배열 length , 문자열 length()
int[] arr = {1, 2, 3, 4, 5};
int length = arr.length; // 배열의 길이
String str = "Hello, World!";
int length = str.length(); // 문자열의 길이
정렬
import java.util.Arrays;
int[] arr = new int[]{12, 41, 37, 81, 19, 25, 60, 20};
Arrays.sort(arr);
Arrays.sort(arr, 시작 Index, 끝 Index + 1)
///////
// 내림차순
import java.util.Arrays;
import java.util.Collections;
public class Main {
public static void main(String[] args) {
Integer[] arr = new Integer[]{12, 41, 37, 81, 19, 25, 60, 20};
Arrays.sort(arr, Collections.reverseOrder());
for(int i = 0; i < 8; i++) // 81, 60, 41, 37, 25, 20, 19, 12
System.out.print(arr[i] + " ");
}
}
즉 int 배열을
// 내림차순 정렬
Integer[] nums2 = Arrays.stream(nums).boxed().toArray(Integer[]::new);
Arrays.sort(nums2, Collections.reverseOrder());
이렇게 하여서 Integer로 바꾸어주어야한다.
//
반대로 Integer를 int배열로
// Integer[]를 int[]로 변환
int[] intArray = Arrays.stream(nums).mapToInt(Integer::intValue).toArray();
-
React
리액트
설치
<git bash>
필수 설치
- node/npm: https://nodejs.org (by NVM / Volta)
$> nvm install --lts
$> nvm use v20.10.0
$> nvm alias default v20.10.0
- npx: $> npx -v # 없다면..
- $> npm i npx -g
- vscode: https://code.visualstudio.com/download
기타
- React DevTools # 리액트 개발자 도구
- yarn: npm i yarn -g # brew install yarn
- vite: https://vitejs-kr.github.io/
- git: https://git-scm.com/download
설정
<git bash>
vite >= node v14.18 or LTS
$> yarn create vite <rbvite>
# yarn create vite rbvite --template react-ts
# npm init vite@latest rbvite # in rb
$> cd rbvite
$> yarn # npm i
$> yarn build # npm run build
$> yarn dev //으로 실행
확장팩
<vscode>
prettier
ESLint
초기 설정
<git bash>
$> yarn add -D eslint-plugin-react
$> yarn add -D eslint-plugin-jsx-a11y # 접근성
<.eslintrc.cjs>
.eslintrc.cjs bold 부분 수정!
module.exports = {
env: { browser: true, es2021: true, node: true },
extends: [
'eslint:recommended', 'plugin:react/recommended',
'plugin:react/jsx-runtime',
'plugin:jsx-a11y/recommended',
],
overrides: [],
parserOptions: {
ecmaFeatures: { jsx: true },
ecmaVersion: 'latest', sourceType: 'module',
},
plugins: ['react-refresh', 'react-hooks', 'jsx-a11y'],
rules: {
'react/react-in-jsx-scope': 'off',
'react/jsx-uses-react': 'off',
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
'react/prop-types': 'off',
'react/no-array-index-key': 'error',
},
settings: {
react: { version: 'detect' }, // for react version warnning
},
};
<git bash>
install
$> yarn add -D prettier eslint-config-prettier eslint-plugin-prettier
$> yarn prettier # package.json > script > "prettier": "prettier --write .",
.eslintrc.cjs bold 부분 추가!
export default {
env: { browser: true, es2021: true, },
extends: [
…,
'plugin:prettier/recommended',
],
overrides: [],
parserOptions: {
…
},
plugins: ['react', 'react-hooks', 'jsx-a11y', 'prettier']],
rules: {
…,
'prettier/prettier': 'error',
quotes: ['error', 'single', { allowTemplateLiterals: true }],
},
…
};
///
.prettierrc 파일 생성!
{
"singleQuote": true,
"jsxSingleQuote": true,
"semi": true,
"trailingComma": "es5"
}
// prettier 기타
"endOfLine": "auto",
"tabWidth": 2,
"useTabs": false,
"printWidth": 100
<eslint.config.js>
import js from '@eslint/…
import react from 'eslint-plugin-react';
import prettier from 'eslint-plugin-prettier';
import eslintConfigPrettier from 'eslint-config-prettier';
export default tseslint.config(
…
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
react, prettier,
},
settings: {
react: { version: '18.3' }, // React version for react plugin
},
rules: {
...js.configs.recommended.rules, // ESLint rules
...react.configs.recommended.rules, // React rules
...react.configs['jsx-runtime'].rules, // JSX rules
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
'prettier/prettier': 'error',
},
},
eslintConfigPrettier
);
<eslint.config.js>
import jsxA11y from 'eslint-plugin-jsx-a11y';
export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
…
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
react,
prettier,
'jsx-a11y': jsxA11y,
},
settings: {
react: { version: '18.3' },
},
rules: {
...
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
'prettier/prettier': 'error',
'jsx-a11y/alt-text': 'error',
},
},
eslintConfigPrettier
);
-
Java 문자열
Java
자바 문자열 핵심
기본 입출력
import java.util.Scanner;
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
char c = sc.next().charAt(0);
System.out.println(); //줄바꿈
System.out.printf("%.2f %d",a,b);//포맷
System.out.print();//그냥 출력
//배열
int[] arr = new int[크기];
int[] arr = new int[3]{1,2,3};
문자열 입력
Scanner sc = new Scanner(System.in);
String str = sc.next(); //공백으로 나누어서 입력받음
String str = sc.nextLine(); // space를 포함해서 입력받음
문자열 관련 코드
str.length()// str문자열의 길이 반환
String a = "apple", b = "banana";
a = a + b; //문자열 합치기 가능1
String a = "apple", b = "banana", c = "candy";
String totStr = "";
totStr += a;
totStr += b;
totStr += c;
//문자열 합치기 가능2
str.charAt(5); //str문자열의 인덱스 5번 반환
문자열.substring(시작 인덱스, 끝 인덱스)//시작 인덱스부터 끝 인덱스 전까지로 이루어진 부분문자열이 반환
s.substring(i, i + 2).equals("ab")//i~i+2가 ab랑 같은지
s.contains("ab")//s라는 문자열이 ab를 가지고 있는지 성공하면 true반환 아니면 false반환
s.indexOf("ab")//s에서 ab라는 해당 부분 문자열이 없는 경우에는 -1을, 있는 경우에는 가장 앞에 나오는 부분 문자열의 위치를 반환
문자열 끼리의 == 연산자는 두 문자열의 주소 값이 일치하는지를 비교하는 연산이기에 목적과는 다릅니다. str1.equals(str2) 함수를 써야한다.
문자열을 배열로 만들기
public class Main {
public static void main(String[] args) {
String s = "baaana";
char[] arr = s.toCharArray(); //문자열을 배열로 만들기
arr[2] = 'n';
s = String.valueOf(arr); //배열을 문자열로 만들기
System.out.println(s);
}
}
>> banana
아스키코드 출력
System.out.println((int)'A');
// A의 아스키코드인 65 출력
Math
Math.abs(숫자);
문자열을 정수로 변환하기
int aInt = Integer.parseInt(a) + 1;
정수를 문자열로 변환하기
Integer.toString(a);
사전순 앞 비교
String a = "abc";
String b = "abd";
String c = "aba";
System.out.println(a.compareTo(b)); // -1
System.out.println(a.compareTo(c)); // 2
System.out.println(a.compareTo(a)); // 0
//compareTo() 함수를 사용합니다. 두 문자열 str1, str2가 주어졌을 때 str1.compareTo(str2) 값이 0보다 작으면 str1이 사전순으로 더 앞서고, 0보다 크면 str2이 사전순으로 더 앞서고, 0이면 두 값이 같음을 의미합니다.
-
-
Amazing JavaScript
Amazing JavaScript
인프런-Amazing JavaScript
강의영상 : https://www.inflearn.com/course/amazing-javascript-%EC%9E%85%EB%AC%B8
강의자료 : https://joshua1988.github.io/vue-camp/
온라인 IDE(코드샌드박스) : https://codesandbox.io/
개념
함수를 선언할 떄 쓰는 값: 파라미터(매개변수)
함수를 호출할 때 쓰는 값: 아규먼트(인자)
화살표 함수
const foo = () => {
console.log("foobar");
};
foo();
객체
자바스크립트는 객체 기반 언어입니다. 객체는 키(key) - 값(value) 형태로 이루어져 있으며 아래와 같은 형태를 갖습니다.
var josh = {
// 속성: 값
language: 'javascript',
coding: function() {
console.log('Hello World');
}
};
배열 API
push() : 배열에 데이터 추가 (맨 끝 인덱스부터 추가됨)
slice() : 배열의 특정 인덱스에 있는 값을 반환 (배열의 내용이 변환되지 않음)
splice() : 배열의 특정 인덱스에 있는 값을 변경 또는 삭제 (배열의 내용이 변경됨)
pop() : 배열의 마지막 인덱스의 값을 꺼냄 (배열의 내용이 변경됨)
shift() : 배열의 첫번째 인덱스의 값을 꺼냄 (배열의 내용이 변경됨)
const array1 = ['a', 'b', 'c'];
array1.forEach((element) => console.log(element));
Map
Map 객체는 키 와 값을 한 쌍으로 이루어진 컬렉션입니다.
// map 선언
const map = new Map();
// map 값 추가 #1
map.set('key1', 'value1');
map.set('key2', 'value2');
// map 값 추가 #2
map
.set('key1', 'value1')
.set('key2', 'value2');
console.log(map); // Map(2) { 'key' => 'value', 'key2' => 'value' }
// map 값 삭제
map.delete('key1')
console.log(map); // Map(1) { 'key2' => 'value2' }
// map 모든 값 삭제
map.clear();
console.log(map); //Map(0) {}
map.forEach((val, key) => {
console.log(val + "," + key);
});
// value 값 가져오기
for (const value of map.values()) {
console.log("value : " + value);
}
// entries 반복문
for (let[key, value] of map.entries()) {
console.log(key + " : " +value);
}
Set
// set 선언
const set = new Set();
// set 값 추가 #1
set.add('javascript');
set.add('vue');
set.add('node');
// set 값 추가 #2
set.add('javascript').add('vue').add('node');
console.log(set); // Set(3) { 'javascript', 'vue', 'node' }
// set 값 삭제
set.delete('banana')
console.log(set); // Set(2) { 'apple', 'orange' }
// set 전체 값 삭제
set.clear();
console.log(set); // Set(0) {}
// set 값 순회
set.forEach((val, val2, set) => {
console.log(val, val2, set);
});
for (const val of set) {
console.log(val);
}
// key 값 가져오기
for (const key of set.keys()) {
console.log(key);
}
// value 값 가져오기 , set 에서 values() 는 keys()와 같습니다.
for (const value of set.values()) {
console.log(value);
}
// 추가된 순서대로 반환됩니다.
for (const [key, value] of set.entries()) {
console.log("key" + " = " + key);
console.log("value" + " = " + value);
}
forEach안에 함수
var arr = [10,20,30];
var newArr = [];
arr.forEach(fuction(item) {
newArr.push(item);
});
filter
원하는 특정요소들을 꺼내옴
배열이름.filter
템플릿 리터럴
` -> 이름은 백틱
이 백틱(`)을 사용해서
`${변수명}` 이렇게 쓸 수 있다
디스트럭쳐링(구조 분해 문법)
var arr = ['apple',10]
var [fruit, num] = arr;
// 로 하면 fruit에 'apple', num에 10이 들어간다
//////
var josh = {
language: 'javascript',
position: 'front-end',
area: 'pangyo',
hobby: 'singing',
age: '102'
};
var { language:별칭, position, area, hobby, age } = josh;
// :별칭 이름을 써서 이름을 바꿀 수 있다
console.log(language); // javascript
console.log(position); // front-end
console.log(area); // pangyo
console.log(hobby); // singing
console.log(age); // 102
스프레드 오퍼레이터
… 으로 분리해서 넣는다
// obj 객체를 newObj 객체에 복제
var obj = {
a: 10,
b: 20
};
var newObj = {...obj};
console.log(newOjb); // {a: 10, b: 20}
// arr 배열을 newArr 배열에 복제
var arr = [1,2,3];
var newArr = [...arr];
//var newArr = [...arr, 30, 50];
//이렇게 추가도 할 수 있다.
console.log(newArr); // [1, 2, 3]
모듈화
Import & Export
// math.js
export var pi = 3.14;
export function sum(a, b) {
return a + b;
}
// app.js
import { pi } from './math.js';
import { sum } from './math.js';
// import { pi, sum } from './math.js';
//한번에 불러와도 된다
console.log(pi); // 3.14
default
파일에서 하나를 꺼내겠다.
{} 이 괄호가 없다.
// math.js
export default var pi = 3.14;
// app.js
import pi from './math.js';
console.log(pi); // 3.14
라이브러리
개발자가 편하게 구현할 수 있도록 미리 만들어 놓은 기능
import 라이브러리이름 from "라이브러리이름"
Lodash 라는 라이브러리 추천함
비동기처리
자바스크립트의 비동기 처리란 특정 코드의 연산이 끝날 때까지 코드의 실행을 멈추지 않고 다음 코드를 먼저 실행하는 자바스크립트의 특성을 의미합니다.
function fetchMenu(callbackFunction){
setTimeout(() =>{
//# 2
let data = {firstMenu: "구독"};
callbackFunction(data);
return data;
}, 5000);
}
let menu;
menu = fetchMenu(function (result) {// 익명함수
// 호출되면 실행될 코드를 넣어주세여
console.log("5초 뒤 실행", result);
});
//#1
console.log("출력 결과", menu);
//아직 값이 안나와서 여긴 언디파인드뜸
Promise - 프로미스
프로미스는 자바스크립트 비동기 처리에 사용되는 객체입니다. 여기서 자바스크립트의 비동기 처리란 ‘특정 코드의 실행이 완료될 때까지 기다리지 않고 다음 코드를 먼저 수행하는 자바스크립트의 특성’을 의미합니다.
https://joshua1988.github.io/web-development/javascript/promise-for-beginners/
.then(); //성공
.catch(); //실패
-
-
-
TypeScript
타입스크립트
함수타입
// Call Signature: (a:number, b:number) => number
function add(a : number, b: number) {
return a + b;
}
add(1);
//Error : Expected 2 arguments, but got 1.
add(1, 2); // Ok
add(1, 2, 3);
//Error : Expected 2 arguments, but got 3.
타입스크립트는 함수에 선언된 모든 매개변수가 필수라고 가정한다!
const introduce = (name:string,
height?:number) => { //?는 옵셔널파라미터 있어도되고 없어도 되고
console.log(`이름 : ${name}`);
console.log(`키 : ${height + 10}`)
//Error : 'height' is possibly 'undefined'.
}
introduce("김현준"); // OK
introduce("김현준", undefined); // OK
introduce("김현준", 170); // OK
함수 호출에 선택적 매개변수를 인수로 제공할 필요 X
선택적 매개변수에는 항상 | undefined 가
유니언타입으로 추가 되어있기 때문!
선택적 매개변수는 끝에 위치해야함
const introduce2 = (name:string,
height : number|undefined ) => {
console.log(`이름 : ${name}`);
if(typeof height === 'number'){
console.log(`키 : ${height + 10}`)
}
}
introduce2("김현준"); // Error : Expected 2 arguments, but got 1.
introduce2("김현준", undefined); // OK
introduce2("김현준", 170); // OK
선택적 매개변수는 | undefined를 포함하는 유니언 타입 매개변수와 다름.
?으로 표시된 선택적 매개변수가 아닌 매개변수는
값이 명시적으로 undefined 일지라도 항상 제공되어야 한다.
const getSum = (...rest: number[]) =>{
//rest로 여러개의 넘버를 받음(넘버만!)
const getSum2 = (a: number,...rest:[number, number]) =>{
//튜플로 rest로 두개의 넘버를 받음
function singSongRecursive(songs : string[], count = 0) : number {
return songs.length ? singSongRecursive(songs.slice(1), count + 1) : count;
}
// 재귀함수에서의 명시적 반환 타입지정
let singer : (song : string) => string;
singer = function(song) {
// song : string의 타입
return `Singing : ${song.toUpperCase()}!`; // OK
}
//타입 스크립트는 선언된 타입의 위치에 제공된 함수의 매개변수 타입을 유추할 수 있다!
void 반환타입
function logSong(song : string) : void {
if(!song){
return; // OK! return undefined; 도 OK!
}
console.log(`${song}`);
return true; // Error! - Type 'boolean' is not assignable to type 'void'.
}
// ← void를 반환하도록 선언되었으므로 값 반환을 허용하지 않음.
// But, 다음과 같이 화살표 함수는 구문 오류 없음!
type VoidReturn = () => void;
const test2:VoidReturn = () => 11; // OK!
test2().toString(); // ?
//일부 함수는 어떤 값도 반환하지 않을 수 있음 -> 타입스크립트는 void 키워드를 사용해 반환값이 없는 함수의 타입을 확인할 수 있음
//void는 그냥 반환을 하려해도 타입스크립트는 검사를 안한다
//fuction은 등록을 해야해서 void를 썻는데 return이 있으면 오류뜨고
//화살표함수는 이미 등록이라서 void인데 return이 있어도 ㅇㅋ
never 반환타입
never 반환 함수는 (의도적으로) 항상 오류를 발생시키거나 무한 루프를 실행하는 함수
function fail(message : string) : never {
throw new Error(`Invariant Failure : ${message}`);
// JS는 return undefined; 가 생략
}
function workWithUnsafeParam(param : unknown){
if(typeof param !== "string"){
fail(`param should be a string, not ${typeof param}`);
}
// 여기에서 param의 타입은 string으로 알려짐
param.toUpperCase();
}
함수 오버로드
동일한 이름에 매개 변수만 다른 여러 버전의 함수를 만드는 것
매개변수의 형태가 다양한 여러 케이스에 대응하는 같은 이름을 가진 함수를 만드는 것
// 서로 다른 버전의 함수들(선언부) -> `오버로드 시그니처`
function func(a : number): void;
function func(a : number, b : number, c : number) : void;
// 실제 구현부 -> `구현 시그니처`
function func(a:number, b?: number, c?:number) {
if(typeof b === 'number' && typeof c === 'number') {
console.log(a + b + c);
} else {
console.log(a * 20);
}
}
func(1);
func(1,2,3);
this
동일한 이름에 매개 변수만 다른 여러 버전의 함수를 만드는 것
매개변수의 형태가 다양한 여러 케이스에 대응하는 같은 이름을 가진 함수를 만드는 것
function f(cb: (this: void) => number) {
return cb();
}
// 이 함수 f는 콜백 함수 cb를 매개변수로 받고 있는데, cb는 this를 사용하지 않는 함수여야 한다는 것을 명시하고 있습니다. 이를 위해 cb의 타입으로 (this: void) => number를 지정했는데, 이는 this가 void 타입임을 나타냅니다. 즉, this를 사용하지 않는 함수여야 한다는 뜻입니다.
const obj = { id: 1 };
function x(this: typeof obj) {
return this.id;
}
function y() {
return 1;
}
f(x); // Fail
f(y); // OK
// f(x):
// 실패하는 이유는 x 함수가 this로 obj를 필요로 하지만, f 함수는 cb의 this가 void라고 명시했기 때문입니다. 즉, f는 this를 사용하지 않는 함수만 받을 수 있는데, x는 this를 사용하므로 타입 호환이 되지 않습니다.
// 오류 메시지에서 설명하듯이, void 타입의 this는 { id: number }와 호환되지 않습니다.
// f(y):
// 성공하는 이유는 y 함수는 this를 사용하지 않으므로, this: void로 선언된 콜백 함수로 사용할 수 있기 때문입니다.
Tip. tsconfig.json 에서 strict: true 가 포함하는 옵션들
{
"strict": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true,
}
//
noUncheckedIndexedAccess : 해당 index의 값이 undefined일 수 있음을 체크하겠다!
이 옵션을 true로 설정하면 인덱스 시그니처에 'undefined'를 포함시킨다!
결합
concat을 이용한 결합
const nums1 = [1, 2, 3, 4, 5];
const nums2 = [10, 20, 30, 40, 50];
const result1 = nums1.concat(nums2);
// 당연히 result1은 number[]
const strings1 = ['lim', 'eun', 'ha'];
const result2 = result1.concat(strings1);// 에러가 뜬다. concat은 결합할 것의 타입이 같아야함
spread를 이용한 결합
const nums1 = [1, 2, 3, 4, 5];
const nums2 = [10, 20, 30, 40, 50];
const result1 = nums1.concat(nums2);
// 당연히 result1은 number[]
const strings1 = ['lim', 'eun', 'ha'];
const result2 = [...result1, …strings1];
// 둘을 결합한 result2는 ?? (string | number)[]
튜플
고정된 크기의 배열. 각 인덱스에 알려진 특정 타입을 갖는다! 즉) 자리수가 고정이 튜플인것이다.
튜플 타입은 가변 길이의 배열보다 더 구체적이다! 따라서, 배열은 튜플 타입의 변수에 할당할 수 없다.
타입스크립트는 튜플의 길이를 알고있다. 따라서 길이가 다른 튜플은 서로 할당할 수 없다!
인수에 배열 스프레드를 사용한 경우 해당 함수는 나머지 매개변수가 꼭 있어야 한다. 나머지 매개변수가 없다면 배열을 전달할 경우 반드시 튜플만 스프레드하여 전달할 수 있다! /** 함수의 매개변수 튜플
인터페이스
타입스크립트에서 인터페이스(interface)는 객체의 구조를 정의하는 데 사용되는 일종의 계약(Contract)입니다. 인터페이스는 객체가 어떤 속성을 가져야 하는지, 그리고 그 속성들이 어떤 타입이어야 하는지를 명시합니다.
interface User {
id: number;
name: string;
age?: number; // 선택적 속성 (optional property)
}
let user1: User = {
id: 1,
name: "Alice"
};
let user2: User = {
id: 2,
name: "Bob",
age: 30
};
//
interface Greet {
(name: string): string;
}
const greetUser: Greet = (name: string) => {
return `Hello, ${name}`;
};
console.log(greetUser("Alice")); // 출력: Hello, Alice
숫자 인덱스 시그니처(경력자 질문으로 많이 물어본다)
왜 오류가 날까?
넘버가 통과를 못해서? 어떻게 해결을 할까?
###
-
TypeScript
타입스크립트?
타입스크립트의 구성 4가지
프로그래밍 언어: 새로운 구문이 포함된 언어
타입 검사기(TypeChecker): 코드의 모든 구성요소(함수,변수 등)를 이해하고 오류 감별
컴파일러(Pre-Compiler): TS Syntax-tree를 JS Code로 생성 (cf. AltJS)
Pre 는 선처리가 된다는 말
언어지원 서비스(IDE): VSCode, SublimeText, Vim 등의 편집기에서 TS 지원
타입스크립트 연습하는 사이트
https://www.typescriptlang.org/play/
Settings의 AST를 check하고, 버전을 v5.1.6으로 하여 AST를 확인하자
타입스크립트는
전역의 i와 함수의 i는 서로 다른 심볼로 같은 변수라도 스코프에 따라서 에러가 날 수도 있고 안날 수도 있다.
타입스크립트 설치하기
$> mkdir ts && cd ts
$> npm init -y
$> npm install -g typescript ts-node
$> tsc -v
$> tsc --init
$> cat tsconfig.json
$> ts-node hello.ts
ts파일을 만들고 git bash에 tsc 입력하면 js파일이 생김
그리고 tsc –watch를 git bash에 입력해두면 ts파일을 수정하면 자동으로 js파일도 바뀜
타입스크립트의 타입
//null
let value: null = null;
//undefined
let value: undefined = undefined;
//boolean
let isDone: boolean = false;
//string
let name: string = "TypeScript";
let greeting: string = `Hello, ${name}`;
//큰따옴표("), 작은따옴표('), 또는 백틱(`)을 사용하여 문자열을 정의할 수 있습니다
//number
let count: number = 10;
let pi: number = 3.14159;
//bigint
let largeNumber: bigint = 123456789012345678901234567890n;
//symbol
let sym1 = Symbol("description");
let sym2 = Symbol("description"); // 서로 다른 Symbol 값
//고유하고 변경 불가능한 값으로, 객체의 속성 키로 사용할 수 있습니다.
타입설정하기
let x: number;
x=10;
console.log("🚀 x:", x);
x='abc';
console.log("🚀 x:", x);
로 하면 x는 number로 되어있기에 abc에서 오류뜸
따라서 let x: number
sring;으로 해줌 > 유니언(union,
(파이프)): 값에 허용되는 타입을 두 개 이상의 가능한 타입으로 확장하는 것
초기값이 없는 변수
타입 undefined로 주자 (any로 주지 말자)
할당 되기 전, 속성 중 하나에 접근 하려고 하는 것처럼 변수를 사용하려고 하면 에러 발생
undefined 유니온 타입일 때 접근 가능한 이유는 undefined 할당 가능성을 미리 알렸기 때문
JS에서는 값을 할당하지 않는다면 초기화 값인 undefined를 참조, TS에선 이를 미리 타입으로 설정해야 참조 가능
=> 결론: undefined 가능성을 미리 알리지 않았기 때문에 할당 없이 접근하려고 했을 때 에러 발생
type 정의
타입스크립트에서 type 키워드를 사용하여 객체의 타입을 정의하는 방법은 매우 유용합니다. 이를 통해 객체 구조를 미리 정의하고, 해당 구조를 여러 곳에서 재사용할 수 있습니다. type 키워드를 사용하여 특정 타입의 형식을 정의할 수 있으며, 인터페이스와 유사하게 동작합니다.
type User = {
id:number;
name:string;
age:number;
address:string;
}
스크립트 파일의 선언 스코프
타입스크립트에서 “스크립트 파일”은 전역 스코프에서 실행됩니다. 즉, 파일 간에 명시적인 import나 export가 없으면, 모든 선언(변수, 함수 등)이 전역 스코프에 존재하게 됩니다. 따라서 한 스크립트 파일에서 선언된 변수가 다른 스크립트 파일과 충돌할 수 있습니다.
스크립트 파일의 특징:
전역 스코프에 존재.
다른 스크립트 파일에서도 동일한 이름의 변수를 선언할 수 없음.
서로 다른 파일에 동일한 변수를 선언하면 충돌이 발생.
// a.ts (스크립트 파일로 간주됨 - import/export 없음)
const shared = 'Cher';
// b.ts (또 다른 스크립트 파일)
const shared = 'Cher'; // 오류 발생: 이미 전역에 같은 이름의 변수가 존재
이 경우 타입스크립트는 shared 변수가 전역 스코프에서 중복 선언된 것으로 판단해 오류를 발생시킵니다.
ECMAScript 모듈의 선언 스코프
모듈 시스템을 사용할 때는 import와 export를 통해 각 파일이 모듈 단위로 동작하게 됩니다. 모듈 파일은 각각 자체적인 스코프를 가지며, 파일 간의 변수 충돌이 발생하지 않습니다. 즉, 파일 간의 이름 충돌은 피할 수 있습니다. 하지만 동일한 모듈 내에서 선언이 충돌하면 오류가 발생합니다.
모듈 파일의 특징:
파일이 모듈로 간주되면, 각 모듈은 자체적인 스코프를 가짐.
같은 이름의 변수를 여러 파일에서 선언해도 충돌이 발생하지 않음.
하지만 같은 파일 내에서 import된 변수와 동일한 이름의 변수를 다시 선언하면 오류가 발생.
// a.ts (모듈 파일)
export const shared = 'Cher';
// b.ts (모듈 파일)
export const shared = 'Cher';
// c.ts (모듈 파일)
import { shared } from './a'; // 'shared'는 'a.ts'에서 import됨
export const shared = 'Cher'; // 오류: 동일한 이름의 변수를 동일 파일에서 다시 선언할 수 없음
스크립트 파일: 전역 스코프를 공유하기 때문에 파일 간 변수 충돌이 발생할 수 있습니다.
모듈 파일: 파일 간에 독립된 스코프를 가지지만, 같은 파일 내에서 동일한 이름의 변수를 선언하면 오류가 발생합니다.
리터럴 타입
리터럴 타입(Literal Types)은 타입스크립트에서 더 구체적인 값으로 변수를 제한할 수 있는 기능입니다. 원시 타입(숫자, 문자열 등)의 특정 값 자체를 타입으로 사용하여, 특정 값만을 허용하는 방식입니다. 이를 통해 타입 안전성을 높이고 코드에서 명확한 제약을 설정할 수 있습니다.
let direction: 'left' | 'right' | 'up' | 'down';
direction = 'left'; // 가능
direction = 'up'; // 가능
direction = 'forward'; // 오류: 'forward'는 허용되지 않음
유니언 타입(|)과 인터섹션 타입(&)
유니언 타입은 두 개 이상의 타입 중 하나를 가질 수 있도록 허용합니다. 즉, 값이 여러 타입 중 하나를 선택할 수 있는 상황에서 사용됩니다.
type A = string | number;
let value: A;
value = "Hello"; // 문자열 할당 가능
value = 123; // 숫자 할당 가능
value = true; // 오류: boolean은 허용되지 않음
인터섹션 타입은 두 개 이상의 타입을 모두 만족해야 합니다. 즉, 타입들이 합쳐져서 모든 타입의 속성이나 메서드를 모두 갖는 하나의 타입이 됩니다.
```typescript
type B = { name: string } & { age: number };
let person: B;
person = { name: “John”, age: 30 }; // 가능
person = { name: “John” }; // 오류: age 속성이 없음
person = { age: 30 }; // 오류: name 속성이 없음
---
### strictNullChecks
엄격한 null 검사 활성화 / 비활성화: null 혹은 undefined 값을 참조/할당 했을 때 타입 에러 발생 여부
⇒ 10억 달러의 실수! (NullPointerException) ⇒ `strict: true`
> 폴더이름config.json의 `"strictNullChecks": true`로 해주자!!!!
엄격한 null 검사 활성화: 다른 타입이 필요한 위치에서 null 혹은 undefined 값을 참조/할당 하는 것을 방지한다! (즉, 활성화해야만 null 및 undefined에 대해한 오류로 부터 안전해진다!)
---
### 할당
```typescript
let xuser: {id: number, name: string};
xuser = {id: 1, name: 'xx'}; // OK
xuser = {id: 1}; // Error: name 속성이 없습니다
xuser = {id: 1, name: 'xx', age: 30}; // Error: {id, name, age}는 {id, name}에 할당할 수 없습니다.
첫 번째 할당에서는 {id: number, name: string} 타입과 정확히 일치하므로 문제가 없습니다.
두 번째 할당은 name 속성이 누락되어 오류가 발생합니다.
세 번째 할당은 추가 속성(age)이 포함되어 있어, 타입스크립트는 예상된 타입에 불필요한 속성이 있다고 판단해 오류를 발생시킵니다. 이는 신선함 검사(freshness check)에 의해 발생한 것입니다.
hong = {id: 1, name: 'Hong', addr: 'Pusan'} as TUser; // OK
as TUser를 사용하면 타입스크립트는 신선함 검사를 비활성화합니다. 이 경우, 타입스크립트는 이 객체가 TUser라고 간주하고, 추가된 속성 addr을 무시합니다. 즉, 타입 단언(assertion)을 사용해 추가된 속성 검사 오류를 무시할 수 있습니다.
CoVariance & ContraVariance
// strictFunctionTypes = true
function f(cb: (input: string | number)=> number) {return cb(1);}
function f2(input: string | number | boolean) {return 1;}
function f3(input: string | number) {return 1;}
function f4(input: string) {return 1;}
f(f2);
f(f3);
f(f4);
f 함수는 매개변수로 (input: string | number) => number 타입의 콜백 함수 cb를 받습니다. 즉, cb는 string 또는 number 타입의 입력을 받을 수 있어야 합니다.
f2는 string | number | boolean 타입의 매개변수를 받을 수 있습니다.
문제: f2는 boolean 타입도 받을 수 있는 콜백 함수이지만, f는 boolean을 허용하지 않습니다. 타입스크립트는 매개변수의 반공변성(contravariance) 때문에 더 넓은 타입(즉, boolean까지 포함하는 f2)을 더 좁은 타입(string | number만 허용하는 f)에 할당하는 것을 허용하지 않습니다.
요약: f2는 boolean 타입까지 허용하므로, f에 사용할 수 없습니다.
그러나 TypeScript는 실제로는 함수 매개변수의 반공변성을 완전히 엄격하게 적용하지 않고, 어떤 상황에서는 더 넓은 타입을 허용합니다. 이 경우, 매개변수 타입이 넓은 함수인 f2를 매개변수 타입이 좁은 함수인 f의 콜백으로 사용해도 오류가 발생하지 않습니다.
이런 허용은 TypeScript가 실무에서 좀 더 유연하게 사용될 수 있도록 일부러 설계된 부분입니다. 즉, TypeScript는 완전한 반공변성을 강제하지 않고, 보다 유연하게 함수 타입을 비교하는 경우가 있습니다.
f3는 string | number 타입을 매개변수로 받으며, 이는 f가 요구하는 타입과 일치합니다.
문제 없음: f3의 시그니처는 f의 요구 사항과 정확히 일치하므로, f(f3)는 정상적으로 작동합니다.
f4는 string만 매개변수로 받습니다.
문제: f4는 number 타입의 입력을 처리하지 못합니다. 하지만 f는 콜백 함수가 string | number 타입의 입력을 처리할 수 있어야 한다고 요구합니다. f는 number를 전달할 수 있지만, f4는 number를 허용하지 않기 때문에 오류가 발생합니다.
요약: f4는 number를 처리할 수 없기 때문에 f(f4)는 허용되지 않습니다.
0910 아침 정리
유니온 A
B는 A 또는 B가 할당 가능하다는 뜻
인터섹션 A&B는 A와B가 합쳐진 것
-
Touch background to close