🚀 기능 요구 사항

레벨 2의 팀 프로젝트 미션으로 SNS(Social Networking Service)를 만들고자 하는 팀이 있다. 팀에 속한 크루 중 평소 알고리즘에 관심이 많은 미스터코는 친구 추천 알고리즘을 구현하고자 아래와 같은 규칙을 세웠다.

  • 사용자와 함께 아는 친구의 수 = 10점
  • 사용자의 타임 라인에 방문한 횟수 = 1점

사용자 아이디 user와 친구 관계를 담은 이차원 배열 friends, 사용자 타임 라인 방문 기록 visitors가 매개변수로 주어질 때, 미스터코의 친구 추천 규칙에 따라 점수가 가장 높은 순으로 정렬하여 최대 5명을 return 하도록 solution 메서드를 완성하라. 이때 추천 점수가 0점인 경우 추천하지 않으며, 추천 점수가 같은 경우는 이름순으로 정렬한다.

제한사항

  • user는 길이가 1 이상 30 이하인 문자열이다.
  • friends는 길이가 1 이상 10,000 이하인 배열이다.
  • friends의 각 원소는 길이가 2인 배열로 [아이디 A, 아이디 B] 순으로 들어있다.
    • A와 B는 친구라는 의미이다.
    • 아이디는 길이가 1 이상 30 이하인 문자열이다.
  • visitors는 길이가 0 이상 10,000 이하인 배열이다.
  • 사용자 아이디는 알파벳 소문자로만 이루어져 있다.
  • 동일한 친구 관계가 중복해서 주어지지 않는다.
  • 추천할 친구가 없는 경우는 주어지지 않는다.

실행 결과 예시

user friends visitors result
“mrko” [ [“donut”, “andole”], [“donut”, “jun”], [“donut”, “mrko”], [“shakevan”, “andole”], [“shakevan”, “jun”], [“shakevan”, “mrko”] ] [“bedi”, “bedi”, “donut”, “bedi”, “shakevan”] [“andole”, “jun”, “bedi”]

내 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
function problem7(user, friends, visitors) {
if (
typeof user !== "string" ||
!Array.isArray(friends) ||
!Array.isArray(visitors)
)
throw new Error(
"user는 문자열, friends는 배열, visitors는 배열이여야 합니다."
);
if (!user.length || user.length > 30)
throw new RangeError("user는 길이가 1 이상 30 이하인 문자열이여야 합니다.");
if (!friends.length || friends.length > 10000)
throw new RangeError(
"friends는 길이가 1 이상 10,000 이하인 배열이여야 합니다."
);
if (visitors.length < 0 || visitors.length > 10000)
throw new RangeError(
"visitors는 길이가 0 이상 10,000 이하인 배열이여야 합니다."
);

// 유저의 친구들
let userFriends = [];
friends.forEach((friend) => {
if (friend.includes(user)) userFriends = [...userFriends, ...friend];
});
userFriends = userFriends.filter((friend) => friend !== user);

// 유저 친구의 친구들 => 알 수도 있는 친구들
let mayKnowUsers = [];
friends.forEach((friend) => {
userFriends.forEach((userFriend) => {
if (friend.includes(userFriend))
mayKnowUsers = [...mayKnowUsers, ...friend];
});
});

mayKnowUsers = mayKnowUsers.filter((mayKnowUser) => {
return mayKnowUser !== user && !userFriends.includes(mayKnowUser);
});

// 유저 점수 계산
const userCount = {};
mayKnowUsers.forEach((mayKnowUser) => {
userCount[mayKnowUser] = (userCount[mayKnowUser] ?? 0) + 10;
});

visitors.forEach((visitor) => {
if (visitor === user || userFriends.includes(visitor)) return;
userCount[visitor] = (userCount[visitor] ?? 0) + 1;
});

// 점수 기준 내림차순 정렬
const sortFunc = (userA, userB) => {
const numA = userCount[userA];
const numB = userCount[userB];

if (numA - numB > 0) return -1;
else if (numA - numB < 0) return 1;
else return userA < userB ? -1 : userA === userB ? 0 : 1;
};

const userId = Object.keys(userCount);

return userId.sort((userA, userB) => sortFunc(userA, userB)).slice(0, 5);
}
  • 유저의 친구들을 구하였다.
  • 유저의 친구들의 친구들을 구하였다. ⇒ 10점을 매기기 위해
  • 방문객과 유저의 친구의 친구들을 순회하면서 점수를 매겼다.
  • 점수를 기준으로 내림차순 정렬과 동일 점수일 때 알파벳 순으로 정렬을 해주었다.

🏓 소감

이 문제는 처음에 한국말을 이해하기 어려워서 오랫동안 고전했던 문제이다. 친구에게 문제를 차근히 설명해보면서 요구사항에 대해 파악할 수 있었다.

  • 시간이 부족하여 6번문제와 7번문제는 리팩터링을 많이 못한 것이 아쉬웠다.
  • 이전까지 sort() 메서드는 단순히 오름차순, 내림차순 정렬에만 사용되는 것이라고 생각했는데, 이번 기회에 sort() 메서드는 두개의 인자를 받아서 -1, 0, 1 값을 return 하는 것을 기준으로 배열의 index를 직접 변경하는 메서드이라는 것을 제대로 알게되었다.

댓글 공유

🚀 기능 요구 사항

우아한테크코스에서는 교육생(이하 크루) 간 소통 시 닉네임을 사용한다. 간혹 비슷한 닉네임을 정하는 경우가 있는데, 이러할 경우 소통할 때 혼란을 불러일으킬 수 있다.

혼란을 막기 위해 크루들의 닉네임 중 같은 글자가 연속적으로 포함 될 경우 해당 닉네임 사용을 제한하려 한다. 이를 위해 같은 글자가 연속적으로 포함되는 닉네임을 신청한 크루들에게 알려주는 시스템을 만들려고 한다.

신청받은 닉네임 중 같은 글자가 연속적으로 포함 되는 닉네임을 작성한 지원자의 이메일 목록을 return 하도록 solution 메서드를 완성하라.

제한사항

  • 두 글자 이상의 문자가 연속적으로 순서에 맞추어 포함되어 있는 경우 중복으로 간주한다.
  • 크루는 1명 이상 10,000명 이하이다.
  • 이메일은 이메일 형식에 부합하며, 전체 길이는 11자 이상 20자 미만이다.
  • 신청할 수 있는 이메일은 email.com 도메인으로만 제한한다.
  • 닉네임은 한글만 가능하고 전체 길이는 1자 이상 20자 미만이다.
  • result는 이메일에 해당하는 부분의 문자열을 오름차순으로 정렬하고 중복은 제거한다.

실행 결과 예시

forms result
[ [“jm@email.com“, “제이엠”], [“jason@email.com“, “제이슨”], [“woniee@email.com“, “워니”], [“mj@email.com“, “엠제이”], [“nowm@email.com“, “이제엠”] ] [“jason@email.com“, “jm@email.com“, “mj@email.com“]

내 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
const getPartialStr = (str) => {
const res = [];
let limit = 2;
while (limit <= str.length) {
const output = [];
let index = 0;
while (true) {
const seperated = [...str].slice(index, index + limit).join("");
index += 1;
if (limit > seperated.length) break;
output.push(seperated);
}
limit += 1;
res.push(...output);
}

return res;
};

const getDuplicatedUser = (arr) => {
let index = 0;
let position = 1;
const res = [];
while (index !== arr.length - 1) {
const set = new Set([...arr[index], ...arr[position]]);
if (set.size !== arr[index].length + arr[position].length) {
res.push(...arr[index].slice(-1), ...arr[position].slice(-1));
}
position += 1;
if (position === arr.length) {
index += 1;
position = index + 1;
}
}
return [...new Set(res)];
};

function problem6(forms) {
if (!forms.length || forms.length > 10000)
throw new RangeError("크루는 1명 이상 10,000명 이하이여야 합니다.");

const users = {};
forms.forEach((form) => {
users[form[1]] = form[0];
});
const nicknames = forms.map((nickname) => nickname[1]);
const partialStrs = nicknames.map((nickname) => getPartialStr(nickname));
const selectedUsers = getDuplicatedUser(partialStrs);

return selectedUsers.map((selectedUser) => users[selectedUser]).sort();
}
  • 닉네임의 첫번째 글자부터 2글자 이상으로 만들 수 있는 모든 부분 문자열을 구하였다.
  • 각 닉네임의 부분 문자열 배열을 비교하여 중복요소가 하나라도 있으면 중복 문자 포함 유저 배열에 등록하였다.
  • 해당 유저의 이메일을 오름차순으로 정렬한 배열을 반환하였다.

🏓 소감

우선 이 문제를 풀면서 가장 큰 성장을 이뤄냈다고 자축하고 싶었다. 왜냐하면 이와 비슷한 부분 문자열 문제를 못풀어서 스트레스를 받았던 적이 있는데 이번 기회에 나름의 방법(?)을 찾아서 구현에 성공하여 기분이 좋았다.

  • 2글자 이상으로 만들 수 있는 부분 문자열을 모두 구했기 때문에 코드가 매우 복잡해졌다. 하지만 2글자 부분 문자열만 구하여 서로 비교하여도 충분히 문제가 없을 상황이기 때문에 이 부분이 좀 아쉬웠다.
  • while 문을 사용할 경우와 for 문을 사용할 경우가 애매했었는데, 이번에 코드리뷰를 진행하면서 while 문은 특정 조건이 변경되는 상황이라면 사용하도록 하고 for 문은 일정 횟수를 반복해야 하는 상황에서 사용하도록 한다는 것을 알게 되어 상황에 맞게 적절히 사용할 수 있을 것 같다.

댓글 공유

🚀 기능 요구 사항

계좌에 들어있는 돈 일부를 은행에서 출금하고자 한다. 돈 담을 지갑이 최대한 가볍도록 큰 금액의 화폐 위주로 받는다.

돈의 액수 money가 매개변수로 주어질 때, 오만 원권, 만 원권, 오천 원권, 천 원권, 오백원 동전, 백원 동전, 오십원 동전, 십원 동전, 일원 동전 각 몇 개로 변환되는지 금액이 큰 순서대로 배열에 담아 return 하도록 solution 메서드를 완성하라.

제한사항

  • money는 1 이상 1,000,000 이하인 자연수이다.

실행 결과 예시

money result
50237 [1, 0, 0, 0, 0, 2, 0, 3, 7]
15000 [0, 1, 1, 0, 0, 0, 0, 0, 0]

내 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function problem5(money) {
if (!money || money > 1000000)
throw new RangeError(
"매개변수는 1 이상 1,000,000 이하의 자연수여야 합니다."
);

const papers = [50000, 10000, 5000, 1000, 500, 100, 50, 10, 1];
let paperCount = [];

papers.forEach((paper) => {
paperCount = [...paperCount, Math.floor(money / paper)];
money %= paper;
});

return paperCount;
}
  • 나머지와 몫을 이용해 반복문을 통해 구현하였다.

🏓 소감

  • map 메서드는 배열의 요소에 콜백함수를 호출하여 그 반환값으로 새로운 배열을 만드는 메서드라고만 생각하고 처음에 papers 배열을 map 메서드를 사용하여 map 메서드 내부에서 money를 변형시켜 비순수한 함수로 구현하였다. 하지만 함수는 외부 변수를 변경하지 않고 순수하도록 작성해야하므로 이를 forEach 메서드로 변경하였다.
  • 여기서는 parseInt 대신에 Math.floor() 메서드를 사용하여 정수로 변형시켜주었다.

댓글 공유

ESLint와 Prettier 설정

카테고리 Settings

📌 ESLint, Prettier란?

ESLint는 vscode에서 코드를 작성하다가 문법적으로 오류가 발생한 곳을 사전에 미리 알려주는 도구이며, prettier는 ESLint가 알려준 오류를 즉각 고쳐주는 도구이다.

🔨 Prettier 사용 방법

프리티어 패키지를 설치한다.

1
npm i -D prettier

프리티어 구성 파일

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// .prettierrc.cjs
module.exports = {
// 화살표 함수 식 매개변수 () 생략 여부 (ex: (a) => a)
arrowParens: "always",
// 닫는 괄호(>) 위치 설정
// ex: <div
// id="unique-id"
// class="contaienr"
// >
htmlWhitespaceSensitivity: "css",
bracketSameLine: false,
// 객체 표기 괄호 사이 공백 추가 여부 (ex: { foo: bar })
bracketSpacing: true,
// 행폭 설정 (줄 길이가 설정 값보다 길어지면 자동 개행)
printWidth: 80,
// 산문 래핑 설정
proseWrap: "preserve",
// 객체 속성 key 값에 인용 부호 사용 여부 (ex: { 'key': 'xkieo-xxxx' })
quoteProps: "as-needed",
// 세미콜론(;) 사용 여부
semi: true,
// 싱글 인용 부호(') 사용 여부
singleQuote: true,
// 탭 너비 설정
tabWidth: 2,
// 객체 마지막 속성 선언 뒷 부분에 콤마 추가 여부
trailingComma: "es5",
// 탭 사용 여부
useTabs: false,
};
  • 추가적인 부분은 공식문서를 참고하면 된다.

프리티어 제외 파일

1
2
3
4
// .prettierignore
dist;
build;
coverage;

🔨 ESLint 사용방법

ESLint 설치 및 초기화

1
npm init @eslint/config

ESLint 구성 파일

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// .eslintre.cjs
module.exports = {
// ...
extends: [
"eslint:recommended",
"plugin:react/recommended",
"plugin:react/jsx-runtime",
"plugin:@typescript-eslint/recommended",
],
settings: {
react: { version: require("react/package.json").version },
},
rules: {
"@typescript-eslint/no-var-requires": "off",
},
};

ESLint 플러그인

1
npm i -D eslint-plugin-react-hooks eslint-plugin-jsx-a11y eslint-plugin-prettier eslint-config-prettier

React 개발에 필요한 패키지와 프리티어 구성 통합을 위한 패키지를 설치한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// .eslintrc.cjs
module.exports = {
// ...
extends: [
"eslint:recommended",
"plugin:react/recommended",
"plugin:react/jsx-runtime",
"plugin:@typescript-eslint/recommended",
"plugin:jsx-a11y/recommended",
"plugin:prettier/recommended",
"prettier",
],
plugins: ["react", "@typescript-eslint", "jsx-a11y"],
};

역시 ESLint 제외파일도 지정할 수 있다.

1
2
3
4
// .eslintignore
dist;
build;
coverage;

📌 저장할 때, prettier 자동으로 조절해주기

vscode에서 Code - Preferences - settings - default formatter - prettier로 설정

댓글 공유

loader(로더) 설정하기

카테고리 Settings

📌 Loader(로더)란?

webpack은 기본적으로 Javascript, JSON 파일만 해석할 수 있다.

webpack이 로더를 사용하여 번들링 할 때에 다른 타입의 파일을 처리하거나 유효한 모듈로 변환하여 애플리케이션에서 사용할 수 있도록 도와준다.

계속 읽기

webpack 설정하기

카테고리 Settings

📌 webpack 이란?

모듈로 관리하는 파일들을 번들링 해주는 툴이다.

🔨 사용방법

명령어 환경 구성

Webpack 모듈 번들러를 명령어 환경에서 사용하기 위해 webpack, webpack-cli를 설치합니다.

1
$npm i -D webpack webpack-cli

번들 명령어 등록

package.json 파일에 webpack 번들링을 수행하는 bundle 명령을 등록합니다.

1
2
3
4
5
6
7
8
9
{
...
"scripts": {
"webpack:config": "webpack --target=browserslist --entry=./src/main.js --output-path=public",
"webpack:dev": "npm run webpack:config -- --mode=development",
"webpack:prod": "npm run webpack:config -- --mode=production"
},
...
}
  • entry 폴더와 output 폴더 경로를 지정해주면 된다.
  • dev는 개발환경 번들링 작업을 위한 명령이고 prod는 배포환경을 위한 번들링 작업이다. prod의 결과물은 배포 최적화되어 있다.
  • target 속성은 어떤 브라우저 환경을 대상으로 번들링할 것인지를 명시해주는 것이다. 프로젝트 루트 위치에 .browserslistrc 파일을 생성한다.
1
2
3
4
> 0.5% in KR
last 2 versions
not dead
ie 11

명령 실행

1
2
3
$npm run webpack:dev

$npm run webpack:prod

🔨 Webpack 구성 파일

기본적인 구성으로도 번들링 훌륭하지만 복잡한 구성을 위해서 구성파일을 별도로 작성해 관리하는 것이 좋습니다.

공통 구성 파일

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// webpack/common.js
import { resolve } from "node:path";

const commonConfig = {
target: "browserslist",
entry: {
main: resolve("src/main.js"),
},
output: {
path: resolve("public"),
filename: "[name].bundle.js",
},
};

export default commonConfig;

개발 구성 파일

1
2
3
4
5
6
7
8
9
10
// webpack/dev.js
import { merge } from "webpack-merge";
import commonConfig from "./common.js";

const devConfig = merge(commonConfig, {
mode: "development",
devtool: "eval-cheap-source-map",
});

export default devConfig;

빌드 구성 파일

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// webpack/prod.js
import { merge } from "webpack-merge";
import commonConfig from "./common.js";

const prodConfig = merge(commonConfig, {
mode: "production",
devtool: false,
output: {
...commonConfig.output,
filename: "[name].min.js",
},
});

export default prodConfig;

개발, 빌드 명령 등록

1
2
3
4
5
6
7
// package.json
{
"scripts": {
"bundle": "webpack bundle -c webpack/dev.js",
"build": "webpack build --progress -c webpack/prod.js"
}
}

이제 기본 webpack 구성이 아닌 직접 생성한 파일을 통해서 번들링 작업을 해줄 수 있다.

🔨 Webpack 개발 서버 구성

Webpack 전용 개발 서버를 구동하기위해 패키지를 설치한다.

1
$npm i -D webpack-dev-server

서버 구성 파일

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// webpack/server.js
const devServer = {
host: "localhost",
port: 5000,
hot: true,
open: false,
compress: true,
liveReload: true,
static: ["public"],
historyApiFallback: true,
client: {
logging: "info",
overlay: true,
reconnect: 3,
},
watchFiles: {
paths: ["src/**/*.*", "public/**/*.*"],
},
};

export default devServer;

서버 구성 파일 불러오기

개발 환경에서 서버 구성 파일을 불러온다.

1
2
3
4
5
6
7
8
9
10
11
12
// webpack/dev.js
import { merge } from "webpack-merge";
import commonConfig from "./common.js";
import devServer from "./server.js";

const devConfig = merge(commonConfig, {
mode: "development",
devtool: "eval-cheap-source-map",
devServer,
});

export default devConfig;

서버 구동 명령어 등록

1
2
3
4
5
6
7
8
9
10
// package.json
{
"scripts": {
"start": "npm run server -- --open",
"dev": "npm run server",
"server": "webpack server -c webpack/dev.js",
"bundle": "webpack bundle -c webpack/dev.js",
"build": "webpack build -c webpack/prod.js"
}
}

HTML Entry 수정

1
2
3
4
5
6
7
8
<!DOCTYPE html>
<html lang="ko-KR">
<head>
<!-- ... -->
<script type="module" src="main.bundle.js"></script>
</head>
<!-- ... -->
</html>

댓글 공유

🚀 기능 요구 사항

어느 연못에 엄마 말씀을 좀처럼 듣지 않는 청개구리가 살고 있었다. 청개구리는 엄마가 하는 말은 무엇이든 반대로 말하였다.

엄마 말씀 word가 매개변수로 주어질 때, 아래 청개구리 사전을 참고해 반대로 변환하여 return 하도록 solution 메서드를 완성하라.

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
Z Y X W V U T S R Q P O N M L K J I H G F E D C B A

제한사항

  • word는 길이가 1 이상 1,000 이하인 문자열이다.
  • 알파벳 외의 문자는 변환하지 않는다.
  • 알파벳 대문자는 알파벳 대문자로, 알파벳 소문자는 알파벳 소문자로 변환한다.

실행 결과 예시

word result
“I love you” “R olev blf”

내 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function problem4(word) {
if (typeof word !== 'string') throw new TypeError('매개변수는 문자열 타입이여야 합니다.');
if (!word.length || word.length > 1000) throw new RangeError('매개변수는 1 이상 1,000 이하의 문자열이여야 합니다.');

const ALPHABET_LENGTH = 26;
const ASCII_NUMBER_CODE_UPPER_A = 65;
const ASCII_NUMBER_CODE_UPPER__Z = 90 ;
const ASCII_NUMBER_CODE_LOWER_A = 97;
const ASCII_NUMBER_CODE_LOWER__Z = 122;

const splitWords = [...word];
const uppers = Array.from({length:ALPHABET_LENGTH}, (_,i) => String.fromCharCode(ASCII_NUMBER_CODE_UPPER_A+i));
const lowers = Array.from({length:ALPHABET_LENGTH}, (_,i) => String.fromCharCode(ASCII_NUMBER_CODE_LOWER_A+i));

const changeOppositeStr = str => {
const codePosition = str.charCodeAt();

return (codePosition >= ASCII_NUMBER_CODE_UPPER_A && codePosition <= ASCII_NUMBER_CODE_UPPER__Z
? [...uppers].reverse()[uppers.indexOf(str)]
: codePosition >= ASCII_NUMBER_CODE_LOWER_A && codePosition <= ASCII_NUMBER_CODE_LOWER__Z
? [...lowers].reverse()[lowers.indexOf(str)]
: str);
}

return splitWords.map(splitedWord => changeOppositeStr(splitedWord)).join('');
}
  • ASCII 코드를 활용하여 AZ, az 를 담은 배열을 생성하였다.
  • map 메서드를 사용하여 각 요소를 반대편 문자열로 바꿔주었다.

🏓 소감

  • 리팩터링을 하기 전 코드는 ASCII 코드를 상수에 할당하지 않고 그대로 비즈니스 로직에 가져다가 사용하지 해당 숫자가 무엇을 의미하는지 파악하기 힘들어 상수에 할당하니 가독성이 높아졌다.
  • changeOppositeStr() 함수 내부에서 reverse()[] 이 부분이 중복되어 함수로 추출하려 했지만, 짧은 코드에 너무 많은 함수가 오히려 더 가독성을 해친다고 판단하여 그대로 두었다.
  • 로직을 ASCII 코드 대신 정규표현식으로 구현하였다면 비즈니스 로직이 훨씬 더 간결해질 것 같기에 다음에 문자열 문제가 나온다면 정규표현식으로도 도전해봐야겠다.

댓글 공유

🚀 기능 요구 사항

배달이가 좋아하는 369게임을 하고자 한다. 놀이법은 1부터 숫자를 하나씩 대면서, 3, 6, 9가 들어가는 숫자는 숫자를 말하는 대신 3, 6, 9의 개수만큼 손뼉을 쳐야 한다.

숫자 number가 매개변수로 주어질 때, 1부터 number까지 손뼉을 몇 번 쳐야 하는지 횟수를 return 하도록 solution 메서드를 완성하라.

제한사항

  • number는 1 이상 10,000 이하인 자연수이다.

실행 결과 예시

number result
13 4
33 14

내 코드

1
2
3
4
5
6
7
8
9
10
11
12
function problem3(number) {
if (number <= 0 || number > 10000 || number !== parseInt(number)) throw new Error('매개변수는 1이상 10,000 이하의 자연수만 입력 가능합니다.');

let clapCount = 0;
for (let i = 1; i < number + 1; i++) {
const splitStr = [...String(i)];

splitStr.forEach(elem => clapCount = elem === '3' || elem === '6' || elem === '9' ? clapCount + 1 : clapCount);
}

return clapCount;
}
  • 매개변수만큼 반복문을 돌면서 숫자 1부터 문자열로 변환한 다음 문자열에 3,6,9가 있으면 clapCount를 1 증가시켜주는 코드를 작성하였다.

🏓 소감

  • parseInt() 메서드 대신 +number를 사용하여 형변환을 시켰으면 어떨까하는 아쉬움이 남는다. 왜냐하면 parseInt() 메서드는 특정 진수법에 해당하는 정수로 반환하는 역할을 하기에 단순히 숫자로 형변환을 위함이 목적이라면 의미가 벗어난다고 생각했다. 마찬가지로 String() 메서드 대신 (i + ‘’)로 형변환 시켜야겠다.
  • 수강생들과 코드리뷰를 하면서 느낀점은 문자열을 다룰 때, 정규표현식을 사용할 수 있다면 좀 더 가독성이 좋은 코드를 작성할 수 있을 것 이라는 생각이 들었다. 지금부터라도 문자열 문제가 나온다면 정규표현식으로 풀어보는 연습을 해야겠다.

댓글 공유

올바른 리드미 작성법

카테고리 git

📌 리드미란?

리드미란, 다른 사람에게 나의 프로젝트가 얼마나 유용한지를 설명하기 위한 소개글이다.

  • 이 프로젝트를 가지고 무엇을 할 수 있는지?
  • 프로젝트를 어떻게 사용할 수 있는지?

위 두가지를 목적으로 작성하는 것이 바로 리드미이다.

💡 좋은 리드미 작성법

기본만 잘 따라도 좋은 리드미처럼 보일 수 있다.

  1. 프로젝트가 하는 일
  2. 프로젝트가 유용한 이유
  3. 프로젝트 시작하는 방법
  4. 프로젝트에 대한 도움을 받을 수 있는 곳
  5. 프로젝트를 유지하고 기여하는 사람

위 5가지가 github에서 언급하는 리드미 작성에 포함되어야 할 정보이다.

💼 오늘 할 일

[x] 리드미 작성 기준에 맞게 리드미 작성하기

bad-readme-case

위 예시는 내가 과제 제출을 위해 처음에 적었던 리드미이다. 이 리드미를 좋은 리드미 작성법에 맞게 개선해보자.

1. 프로젝트가 하는 일

이 프로젝트는 사용자가 다양한 상태의 업로드 버튼 UI가 필요할 때, 재사용 가능한 컴포넌트를 제공하는 프로젝트이다.

2. 프로젝트가 유용한 이유

  • 재사용이 가능한 업로드 버튼 UI를 사용할 수 있습니다.
  • 기본 제공되는 버튼 UI가 훌륭합니다.

3. 프로젝트 시작하는 방법

UploadButton

기본적인 사용법은 다음과 같습니다.

1
<UploadButton type="idle">업로드</UploadButton>

타입 설정

아래의 타입을 설정하면 다음과 같은 모양의 버튼 UI가 생성됩니다.

type shape
idle status=idle
pending status=pending
resolved status=resolved
rejected status=rejected
disabled status=disabled

4. 프로젝트에 대해 도움 받을 수 있는 곳

만약 프로젝트에 대해 도움이 필요하시다면 아래 이메일로 연락을 주세요!

klj9939@gmail.com

5. 프로젝트를 유지하고 기여하는 사람

https://github.com/loco9939
gitprofile

⭐️ 결과

https://github.com/loco9939/zero-base/tree/icon-upload-button/complete

🏓 소감

리드미란 처음 내 프로젝트를 다른 사람에게 알리는 첫 게시물이므로 사람들이 내 프로젝트를 어떻게 하면 편리하게 사용할 수 있을지, 또 사용하고 싶도록 만들지를 생각하면서 작성해야하는 것을 알게되었다.

이것을 정말 쉽지 않다. 게다가 내 프로젝트에 대해서 정리하여 남에게 설명하는 연습이 잘 되어있지 않아서 낯설고 힘들었지만 아직은 처음이니깐 앞으로 프로젝트를 하게된다면 오늘 배운 점을 고려하면서 작성하도록 노력할 것이다.

댓글 공유

boj-1157 단어공부

카테고리 Algorithm, boj

📌 문제

알파벳 대소문자로 된 단어가 주어지면, 이 단어에서 가장 많이 사용된 알파벳이 무엇인지 알아내는 프로그램을 작성하시오. 단, 대문자와 소문자를 구분하지 않는다.

입력

첫째 줄에 알파벳 대소문자로 이루어진 단어가 주어진다. 주어지는 단어의 길이는 1,000,000을 넘지 않는다.

출력

첫째 줄에 이 단어에서 가장 많이 사용된 알파벳을 대문자로 출력한다. 단, 가장 많이 사용된 알파벳이 여러 개 존재하는 경우에는 ?를 출력한다.

입력 출력
Mississipi ?
zZa Z
z Z
baaa A

내 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const input = require("fs").readFileSync("/dev/stdin").toString().trim();

const alphabets = [...new Set([...input.toLowerCase()])];
let max = 0;
let current = ''

alphabets.forEach((alphabet,i) => {
const regExp = new RegExp(alphabet, 'ig');
const count = input.match(regExp).length;
if (max < count) {
max = count;
current = alphabet.toUpperCase();
}
else if (max === count && alphabet.toUpperCase() !== current) current = '?';
})
console.log(current)
  1. 입력값의 알파벳을 소문자로 만들고 중복을 제거해주었다.
  2. 해당 알파벳을 forEach 문으로 순회하면서 초기에 입력값으로 주어진 문자열을 정규표현식으로 확인한다.
  3. 정규표현식에 매치되는 요소의 갯수가 최대값으로 갱신해주고 그 때의 알파벳을 대문자로 저장해둔다.
  4. 만약 새로운 알파벳의 갯수가 최댓값과 같고 현재의 알파벳 대문자와 current에 할당된 알파벳이 같으면 current에 “?”를 할당한다.

🏓 소감

문자열을 풀 때, 정규표현식을 사용하면 반복문을 줄이고 코드를 깔끔하게 하여 가독성을 키우는 연습을 하는데 도움이 될 것 같다.

주어진 테스트 케이스를 다 만족했지만, 제출했을 때 오답이라고 나와서 당황했다. 어떻게 하면 내 제출이 만족 못시키는 케이스가 있을까하고 생각해보면서 여러가지 상황을 제시하면서 테스트 케이스를 찾아내었다.

그 결과 처음 정규표현식을 생성할 때, alphabets 배열을 순회하면서가 아닌 초기 입력값의 순회한 요소를 넣어주었기 때문에 제대로 된 결과가 나오지 않는다는 것을 깨닫고 수정하였다.

테스트 케이스를 어떻게 찾아내지라고 생각하면서 막막했었는데,

  1. 주어진 문제에서 제한사항 중 내가 놓친 부분이 있는지를 확인한다.
  2. 조건을 만족시키는 다양한 상황을 만들어본다.

위 두가지를 실행해보다보니 찾게되어서 기뻤다.

댓글 공유

loco9939

author.bio


author.job