Namgung Jong Min

토끼굴을 개척하는 개발자

이번 포스트에서는 정렬 알고리즘의 가장 기본이 되는 세 가지의 알고리즘을 알아보고 같은 문제를 각각의 알고리즘을 적용하여 풀이해보겠습니다. O(n^2)의 시간복잡도를 가지는 효율이 낮은 정렬 방법들이지만, 코딩테스트 문제의 시간 조건이 충분할 때 간단하게 구현할 수 있습니다.

▪︎ 선택 정렬

Untitled

선택 정렬은 배열의 모든 요소를 비교하여 해당되는 위치에 요소를 삽입함으로써 정렬을 완성하는 알고리즘입니다.

  1. 첫 요소를 기준삼아 시작합니다.
  2. 배열에서 최솟값을 찾아 첫 요소와 교환합니다.
  3. 두 번째 요소부터 시작해 최솟값을 찾아 두 번째 요소와 교환합니다.
  4. 2의 과정을 매 위치에서 반복합니다.

▪︎ 삽입 정렬

Untitled

삽입 정렬은 매 순서마다 해당 요소를 삽입할 위치를 찾아 정렬을 완성하는 알고리즘입니다.

  1. 정렬되지 않은 첫 요소를 목표로 진행합니다.
  2. 두 번째 요소가 첫 번째 요소보다 작다면 자리를 교환합니다.
  3. 세 번째 요소와 두 번째 요소를 비교하여 교환하고, 교환했다면 그 이전 요소와 다시 비교 / 두 번째 요소보다 크다면 네 번째 요소로 넘어갑니다.
  4. 2의 과정을 매 위치에서 반복합니다.

▪︎ 버블 정렬

Untitled

버블 정렬은 인접한 두 요소를 비교하여 정렬하는 알고리즘입니다. 가장 큰 요소부터 마지막 인덱스에 위치시키기 때문에 요소의 이동이 마치 거품이 수면으로 올라오는 듯한 모습을 보입니다.

  1. 정렬되지 않은 마지막 요소를 목표로 진행합니다.
  2. 첫 요소와 다음 요소를 비교 첫 요소가 크다면 두 번째 요소와 자리를 교환합니다.
  3. 두 번째 요소와 그 다음 요소를 비교 두 번째 요소가 크다면 세 번째 요소와 자리를 교환합니다.
  4. 동작을 반복하면 마지막 요소에 배열의 가장 큰 값이 위치하게 됩니다.
  5. 이후 1-3 과정을 반복합니다.

▪︎ 문제 적용

Untitled

▫︎ 선택 정렬 풀이

1
2
3
4
5
6
7
8
9
10
11
12
13
function solution(arr) {
let answer = [...arr];

for (let i = 0; i < arr.length; i++) {
for (let j = i; j < arr.length; j++) {
if (answer[i] <= answer[j]) continue;

[answer[i], answer[j]] = [answer[j], answer[i]];
}
}

return answer;
}

▫︎ 삽입 정렬 풀이

1
2
3
4
5
6
7
8
9
10
11
12
function solution(arr) {
const answer = [...arr];

for (let i = 1; i < answer.length; i++) {
for (let j = i; j > 0; j--) {
if (answer[j] < answer[j - 1]) [answer[j], answer[j - 1]] = [answer[j - 1], answer[j]];
else break;
}
}

return answer;
}

▫︎ 버블 정렬 풀이

1
2
3
4
5
6
7
8
9
10
11
12
13
function solution(arr) {
let answer = [...arr];

for (let i = arr.length - 1; i > 0; i--) {
for (let j = 0; j < arr.length - i; j++) {
if (answer[j] <= answer[j + 1]) continue;

[answer[j], answer[j + 1]] = [answer[j + 1], answer[j]];
}
}

return answer;
}

코딩 테스트 문제 중에 데이터를 순회화며 결과를 키 값에 저장해야 할 때가 있습니다. 배열의 인덱스처럼 의미를 부여하여 사용하는 것이 아닌 의미를 지닌 키 값에 직접적으로 데이터를 매칭시킬 수 있는 방법이며, 검색에서의 시간복잡도 또한 O(1)로 효율적인 알고리즘입니다.

▪︎ 해시 알고리즘 (Hash)

기본적으로 배열에 key: value 값으로 데이터를 저장하면 구현이 가능합니다.

1
2
3
4
5
6
7
const hashArray = [];

hashArray["bus"] = 5;
hashArray["price"] = 1000;

console.log(hashArray["bus"]); // output: 5
console.log(hashArray); // output: [ "bus": 5, "price": 1000 ]

자바스크립트의 배열은 다른 언어의 배열과는 다릅니다. 다른 언어의 경우는 데이터가 연속적으로 나열되어 구성되는 밀집 배열이고, 자바스크립트는 희소 배열입니다. 즉 여러 개의 자료형을 허락하며 각 자료가 차지하는 메모리 공간 또한 불규칙 할 수 있습니다. 때문에 위의 방식으로도 배열을 만들 수가 있습니다.

그러나 자바스크립트에서는 더 쉽게 테이블을 사용할 수 있게하는 Map이라는 내장 함수가 있습니다. Map에서 제공되는 여러 프로토타입 메서드를 활용하면 해시 구조에서 원하는 기능을 보다 쉽게 사용할 수 있습니다.

▫︎ Map 함수의 여러 메서드

  1. new Map() : 새로운 Map 객체를 만듭니다.
  2. map.set(key, value) : Map 객체 내의 key와 value를 매핑하여 저장합니다.
  3. map.get(key) : Map 객체에서 key에 해당하는 value를 반환합니다.
  4. map.has(key) : Map 객체 내에 key가 존재한다면 true, 존재하지 않는다면 false를 반환합니다.
  5. map.delete(key) : Map 객체 내에서 key와 매핑된 value 모두를 삭제합니다.
  6. map.size : Map 객체의 요소 수를 반환합니다.
  7. map.forEach(callbackFn(key, value)) : 각 value와 key마다 callbackFn을 삽입한 순서대로 실행합니다.

▪︎ Example of Apply 1

Untitled

▫︎ 문제 접근

  1. 각 후보에 대한 데이터를 생성한다.
  2. 각 후보가 투표를 받을 때마다 데이터를 갱신한다.
  3. 가장 큰 값을 가진 후보를 출력합니다.

위 문제에서 후보에 대한 정보를 저장할 때 key 값으로 후보를 지정하는 것이 좋아보입니다. ( 배열의 index에 의미를 부여하여 사용할 수 있지만 가독성이 좋지 않습니다.) Map 객체를 만들고 각 key 값으로 후보를 등록한 뒤 주어진 개표 결과를 순회하며 value를 업데이트 시켜주면 쉽게 풀 수 있습니다. 이후에는 map.forEach 메서드를 통해 Map 객체를 순회하며 가장 높은 value를 지닌 key를 답으로 등록합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function solution(s) {
let answer = "";
let max = 0;
let arr = s.split("");
let map = new Map();

arr.forEach((el) => map.set(el, map.get(el) + 1 || 1));
map.forEach((value, key) => {
if (value > max) {
answer = key;
max = value;
}
});

return answer;
}

▪︎ Example of Apply 2

첫 번째 예시 문제가 key : value 형태로 데이터를 저장하기 위해 Map을 사용했다면, 이번 예시는 key에 접근할 때 효율성을 높여 시간복잡도를 줄이기 위해 적용하는 문제 예시를 보겠습니다.

image.png

image.png

위 문제는 [4, 1, 5, 2, 3] / [1, 3, 7, 9, 5] 두 배열을 순회하여 겹치는지 확인만하면 되는 간단한 문제입니다. 그런데 N의 입력값 범위를 살펴보면

1 ≤ N ≤ 100,000 으로 이중 순회로 구현하여 O(n^2)의 시간복잡도로 풀 경우, 시간 제한에 걸리게 됩니다. Map 내장 함수의 경우 key에 접글할 때 O(1)의 시간복잡도를 가지기 때문에 하나의 배열을 Map으로 만들고 나머지 배열 하나를 순회하며 Map에 key 값이 있는지 여부만 판단하면 조건에 맞게 풀 수 있습니다.

  • 이중 순회로 구현 (시간 초과)
1
2
3
4
5
6
7
8
const fs = require("fs");
const input = fs.readFileSync("dev/stdin").toString().trim().split("\n");
const N = input[1].split(" ").map((v) => +v);
const M = input[3].split(" ").map((v) => +v);

for (let i = 0; i < M.length; i++) {
N.includes(M[i]) ? console.log(1) : console.log(0);
}
  • array.prototype.includes() 메서드는 O(N)의 시간복잡도를 가진다.
  • Map 함수 적용하여 구현 (조건 통과)
1
2
3
4
5
6
7
8
const fs = require("fs");
const input = fs.readFileSync("dev/stdin").toString().trim().split("\n");
const N = input[1].split(" ").map((v) => +v);
const M = input[3].split(" ").map((v) => +v);
const map = new Map();

N.forEach((el) => map.set(el, true));
M.forEach((el) => (map.has(el) ? console.log(1) : console.log(0)));

코딩테스트 문제에서 주어진 데이터들을 순회할 때, 답을 찾기 위해 직접적으로 순회하면 시간 조건을 초과하는 경우가 있습니다. 이 때 시간 복잡도를 줄이기 위해 투 포인터와 이진 검색을 고려합니다. 두 개의 포인터로 정답 도출이 가능할 것 같을 때에 투 포인터 알고리즘을 적용하고, 불가능 할 때에는 이진 검색을 적용합니다.

▪︎ 이진 검색

이진 검색은 오름차순으로 정렬된 리스트에서 특정한 값의 위치를 찾는 알고리즘입니다. 탐색 범위를 절반씩 줄여나가면서 값을 찾기 때문에 빠른 속도를 보장합니다.

Untitled

위 배열에서 121이라는 숫자가 몇 번째 index에 있는지를 도출해야한다고 생각해봅시다. index 0부터 순회하며 121을 찾을 수 있지만 배열의 범위를 보면 121이 아닌 더 큰 값이 입력으로 들어왔을 경우 최악의 경우 10^9 번의 동작을 순회해야 합니다. 완전 탐색 포스팅에서 확인했듯이 코딩 테스트에서 우리는 10^8 안으로 시행 횟수를 기준으로 생각하기 때문에 이 방법은 불가능 합니다.

그러면 이진 검색을 활용하면 어떨까요? 최악의 경우에도 30번의 시행만으로 답을 찾아낼 수 있습니다.

▫︎ 이진 검색 구현

  1. 인덱스의 최소값 / 최대값을 변수로 선언합니다.
  2. 범위 내 중간값을 지닌 인덱스에 위치한 요소를 판단하여 121보다 크다면 최대값을 줄여 범위를 좁히고, 작다면 최소값을 높여 범위를 좁힙니다.
  3. 121을 도출할 때까지 2번의 과정을 반복합니다.
1
2
3
4
5
6
7
8
9
10
11
12
let start = 0;
let end = 1000000000;
let answer;

while (start <= end) {
let mid = Math.floor((start + end) / 2);

if (array[mid] <= 121) {
answer = mid;
start = mid + 1;
} else end = mid - 1;
}

기본적인 구현 방법은 위와 같고, 도출할 값의 특성에 따라 조금씩 바뀔 수 있습니다. 예를 들어 어떠한 조건에 맞는 최소값을 찾아야 한다면 도출되는 값은 여러개가 될 것이므로 계속 해서 답을 업데이트해야 합니다. 이 때 어떠한 조건문에 답을 업데이트 하는 로직을 추가할지 잘 생각해야 합니다. 위 이진 검색의 구현 코드는 ‘기본적으로 이렇게 작성된다’라고 생각하며 넘어가고 실제 문제 풀이를 통해 어떤식으로 코드가 작성되고 진행되는지 살펴보겠습니다.

▫︎ 최소 / 최대값 도출하기

때때로 코딩테스트에서 범위에 속하는 값들 중 최소 / 최대값을 찾아야하는 경우도 있습니다. 이 경우 위 코드를 조금 변형하여 풀어낼 수 있습니다. 이 때 answer에 mid값을 할당하는 코드의 위치가 중요합니다. 최소값을 구할 때는 end값을 조정할 때 answer값을 재할당 해야하고, 최대값을 구할 때는 start값을 조정할 때 answer를 재할당 해야합니다. 밑에 작성할 문제 풀이를 통해 확인해봅시다.

▪︎ Example of Apply

Untitled

모든 문제에서 처음 완전 탐색이 가능한지부터 생각합니다. 위 문제에서는 N 과 M에 따라 데이터를 몇번이나 순회할지 결정되어집니다. 입력 설명을 볼 때 N의 범위가 1000보다 작습니다. M에 따라 최악의 경우 1000개를 순회하면서 각 요소들을 어떤 DVD에 넣을지를 분기한다면, 시행 횟수는 2^8을 가볍게 넘을 것입니다. 따라서 이진 검색을 통해 나올 수 있는 정답의 범위를 정하고 조건에 부합하는 값 중 최소값을 찾아보겠습니다.

▫︎ 문제 접근

  1. 한 노래를 쪼개서 두 개의 DVD에 녹화할 수 없기 때문에, 용량 크기는 최소한 주어진 배열의 요소 중 최대값보다는 커야한다.
  2. 모두 한 DVD에 넣는 경우보다 용량이 커질 경우는 없다. 따라서 곡의 길이를 전부 합한 값이 최대값이다.
  3. 이진 검색을 통해 조건을 판별한다.
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
function solution(m, arr) {
let answer = 0;
let start = Math.max(...arr);
let end = arr.reduce((acc, cur) => acc + cur);

while (start <= end) {
let mid = Math.ceil((start + end) / 2);
let sum = 0;
let count = 1;

for (let x of arr) {
if (sum + x <= mid) sum += x;
else if (sum + x > mid) {
count++;
sum = x;
}
}

if (count <= m) {
end = mid - 1;
answer = mid;
} else if (count > m) start = mid + 1;
}
return answer;
}

코딩테스트에서 배열을 순회해야 할 때, 입력값의 조건이 시간 제한에 걸려 단순 순회가 불가능한 경우가 있습니다. 이 때 고려해야할 알고리즘은 ‘투포인터’ 와 ‘이분 탐색’ 입니다. 이 포스팅은 투포인터 알고리즘을 통해 해당 문제를 해결하는 방법을 다루고 있습니다.

▪︎ 투 포인터 알고리즘 (Two-Pointer)

투 포인터 알고리즘은 주어진 배열에서 각각 다른 원소를 가리키는 2개의 포인터를 조작하면서 원하는 값을 도출할 때까지 탐색하는 알고리즘입니다. 각 포인터들의 위치를 정하고 이동하면서 포인트의 위치를 기록하고 처리합니다.

▫︎ 시간 복잡도

순회마다 항상 두 포인터 중 하나는 1씩 증가합니다. 각 포인터는 최대 N까지 증가할 수 있습니다. 기존 배열의 순회에서는 2중 for문을 통해 두 지점을 특정해야하기 때문에 O(N^2)의 시간 복잡도를 지닌 반면, 투포인터 알고리즘을 사용하게 되면 한번의 단순 순회를 통해 포인트들을 이동하면서 처리하기 때문에 O(N)의 시간 복잡도로 문제를 해결할 수 있습니다.

▪︎ Example of Apply

Untitled

▫︎ 문제 접근

문제 해결을 위한 스텝을 나눠보면 다음과 같습니다.

  1. 공통 원소를 찾는다.
  2. 오름차순으로 출력한다.

1번을 해결하기 위한 가장 쉬운 방법은 두 배열 중 하나를 순회하면서 나머지 배열에 현재 인덱스의 요소들이 있는지 판별하는 것입니다. 이후 판별된 요소들만 오름차순으로 정리하면 다음과 같이 풀이할 수 있습니다.

1
2
3
4
5
6
7
8
9
let arr1 = [1, 3, 9, 5, 2];
let arr2 = [3, 2, 5, 7, 8];
let answer = [];

for (let i = 0; i < arr1.length; i++) {
if (arr2.includes(arr1[i])) answer.push(arr1[i]);
}

console.log(answer.sort((a, b) => a - b)); // output: [2,3,5]

그러나 입력값을 기준으로 시간 복잡도를 판단해보면 O(N^2)의 시간복잡도를 가진 위 코드는 최악의 경우 10^8 이내의 실행되지 못하는 코드입니다. 따라서 O(N)의 시간 복잡도로 해당 문제를 해결해야 합니다.

▫︎ 투 포인터 알고리즘으로 접근

  1. 주어진 배열들을 정렬한다.
  2. 각 배열의 첫 인덱스 요소를 포인터로 가리킨다. (p1, p2)
  3. p1 인덱스 요소와 p2 인덱스 요소를 비교하며 같다면 answer array에 추가하고 다르다면 p2를 이동한다. 이 때 p2가 p1보다 크다면 p1의 위치를 이동시킨다.

▫︎ 풀이

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function solution(arr1, arr2) {
let answer = [];
let p1 = 0;
let p2 = 0;

arr1.sort((a, b) => a - b);
arr2.sort((a, b) => a - b);

while (p1 < arr1.length) {
if (arr1[p1] === arr2[p2]) {
answer.push(arr1[p1++]);
} else {
p2++;
}

if (p1 < p2) {
p1++;
p2 = 0;
}
}

return answer;
}

▪︎ 누적합 알고리즘

image.png

누적합 알고리즘은 이전 누적합에 대해 현재 인덱스의 값을 더하여 구하는 방법입니다. 기본적인 방식으로 합을 구하게 되면

1 / 1+2 / 1+2+3 / 1+2+3+4 / 1+2+3+4+5 의 방식대로 값을 구하게 되는데 이에 비해 훨씬 효율적이며 구간합을 구할 때 이중 순회를 거치지 않고 누적합을 저장한 데이터를 토대로 최종 인덱스와 시작 인덱스의 설정만으로 답을 찾아낼 수 있습니다. 적용 예시를 보면 쉽게 이해하실 수 있습니다.

▪︎ Example of Apply

image.png

image.png

위 문제는 배열의 13번째 누적합, 24번째 누적합, 5~5번째 누적합을 구하면되는 간단한 문제입니다. 그러나 저 세 케이스를 순회하면서 다시 for문을 통해 원소들을 합하게되면 O(N^2)의 시간복잡도로 최악의 경우 10^10의 시행횟수를 갖게되어 1초 (10^8) 시간제한을 통과할 수 없습니다. 이 때 누적합 알고리즘을 적용하여 주어진 배열의 누적합을 따로 데이터로 저장해놓고 단순 접근과 연산만으로 답을 찾아낼 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const fs = require("fs");
const input = fs
.readFileSync("dev/stdin")
.toString()
.trim()
.split("\n")
.map((el) => el.split(" ").map((v) => +v));

const [N, M] = input[0];
const numbers = input[1];
const arr = input.slice(2);
const prefixSum = Array(N + 1).fill(0); // 문제에서 인덱스가 기준이 아닌 몇번 째인지로 판단하기 때문에 1부터 데이터를 저장
let answer = [];

// 누적합 데이터를 생성
for (let i = 0; i < N; i++) {
prefixSum[i + 1] = prefixSum[i] + numbers[i];
}
// 누적합 데이터를 단순 연산하는 것 만으로 구간합 도출
for (let i = 0; i < arr.length; i++) {
answer.push(prefixSum[arr[i][1]] - prefixSum[arr[i][0] - 1]);
}

console.log(answer.join("\n"));

코딩테스트 대부분의 문제에서 주어진 자료를 순회하여 답을 도출하는 경우가 많습니다. 이 때 어떠한 알고리즘으로 순회를 하여 문제에 접근하는 것이 좋을지 1초의 제한 시간 기준을 통해 판별하는 방법을 정리한 글입니다.

▪︎ 시간 복잡도를 고려한 기준 적용

대부분의 코딩테스트 시험의 시간 제한 조건은 1초~5초 내외입니다. 따라서 실행 시간을 1초 내로 줄이는 것을 목표로 문제에 접근하는 것이 코딩테스트를 준비하는 데 있어 합리적입니다.

테스트를 하는 각 서버의 CPU에 따라서 같은 코드라도 실행 시간이 천차 만별이므로 “엄격하게 시간 복잡도를 몇으로 해야된다”라고 규정하는 것은 어렵습니다. 그러나 관행적으로 10^8 이내의 시행 횟수를 1초 내외의 제한 시간을 통과한 것으로 인정하고 그에 맞게 설계를 하는 편입니다.

데이터를 순회하여 정답을 도출해야하는 문제에서는 제일 먼저 완전 탐색(Brute Force)을 고려합니다. 이 때 전체적인 풀이의 흐름을 그려보고 시행 횟수를 빠르게 판단해보는 것이 좋습니다. 10^8 이내의 시행 횟수로 정답 도출이 가능하다면 완전 탐색을 적용하고, 불가능하다면 다른 알고리즘을 고려해보아야 합니다.

▪︎ Example of Apply

Untitled

▫︎ 문제 접근

이 문제에서 우리는 총 3번의 순회가 필요합니다.

  1. 2번의 반복문을 통한 멘토 학생과 멘티 학생이 매칭
  2. 1번의 반복문을 통한 테스트 결과 순회

가장 먼저 완전 탐색을 고려하여 10^8 이내의 시행 횟수로 정답 도출이 가능한지 확인합니다. 제시된 입력 설명에서 최악의 시행 횟수 경우를 산정해보면 202010 입니다. 따라서 다른 알고리즘의 고려 없이 바로 완전 탐색을 적용 가능합니다.

▫︎ 풀이

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function solution(test) {
let answer = 0;

for (let i = 1; i <= test[0].length; i++) {
for (let j = 1; j <= test[0].length; j++) {
if (i === j) continue;

let isMatched = true;
for (let k = 0; k < test.length; k++) {
if (test[k].indexOf(i) > test[k].indexOf(j)) {
isMatched = false;
break;
}
}

if (isMatched) answer += 1;
}
}

return answer;
}

▫︎ 시간복잡도가 충분하지 않다면

  1. 포인터 두 개로 두번의 순회를 한번으로 줄일 수 있다면 ⇒ 투포인터 사용
  2. 순회 내에서 배열의 조작 메서드의 O(n)의 시간복잡도를 그 이하로 낮추려면 ⇒ 자료구조 사용
    • 우선순위 큐 / 연결리스트
  3. 배열 탐색을 O(1)로 하고 싶다면 ⇒ 해시 (Map 함수) 사용
  4. 정렬된 데이터를 기준으로 답을 찾을 수 있을 것 같다면 ⇒ 이진 검색 사용

위의 예시들은 제가 코딩테스트에서 완전 탐색으로 접근할 때 시행횟수가 크다면 고려하는 다음 접근들입니다. 각 케이스 별 정리와 예시 문제 풀이는 알고리즘 카테고리 내 포스팅에서 확인하실 수 있습니다.

파이널 프로젝트가 끝나면서 7개월의 부트캠프 과정이 끝이 났습니다. 길다면 길고 짧다면 짧을 수 있는 시간인데 부트캠프를 진행하기 이전의 저와 수료한 이후의 저는 완전 다른 사람이 된 것 같아요. 사실 처음 시작할 때는 기본적인 클론 코딩도 버거워하던 저였는데 이제는 혼자서 프로젝트를 기획하고 개발하면서 이번엔 어떤 기술을 적용해볼까 행복한 고민을 하고 있어요. 이번 포스팅은 저번에 포스팅 했던 파이널 프로젝트의 결과를 공유해보고, 제가 패스트캠퍼스 X 야놀자 부트캠프를 진행하면서 어떻게 달라졌는지 회고해보려고 해요. 추가로 좋은 소식도 하나 자랑해보려고 합니다. ㅎㅎ.

파이널 프로젝트 끝!

패스트캠퍼스 X 야놀자 부트캠프에서 진행한 RFP기반 기업 연계 프로젝트가 끝이 났습니다. 이번 프로젝트는 확실히 기획 분들과 디자이너 분이 계셔서 프론트엔드에서 와이어프레임을 짜고 디자인 했을 때보다 확실히 결과가 잘 나온 것 같아요! 만들어진 사이트를 보면서 “와! 진짜 서비스하고 있는 사이트처럼 생겼다”라고 생각했습니다. 저희가 부트캠프의 마지막을 장식한 프로젝트 결과물을 함께 감상해봐요.

사이트 핵심 기능

저번 포스팅에서 투표 기반 여행 일정 앱을 만든다고 했었죠? 우리 앱의 핵심 기능은 다음과 같습니다. 하나씩 살펴봐요.

1) 여행지 투표

2) 투표 기반 일정 작성

3) 루트 최적화

4) 여행 정보 검색

랜딩 페이지 생성 및 마케팅

기획분들 덕분에 마케팅도 경험할 수 있었어요. 랜딩 페이지를 만들고 인스타그램과 페이스북 같은 SNS 마케팅을 통해 사람들에게 저희 앱을 노출시키고 랜딩페이지로 유도하여 사전 예약을 받게 하였습니다.

약 일주일 간 랜딩페이지 방문자 413명, CTA 입력폼 섹션 도달 인원 50명, 사전 출시 알림 신청 인원 41명으로 전환율 10%를 달성했습니다!

트립 보트 소개 영상


https://www.youtube.com/watch?v=9d4K_1AzSAA

부트캠프 후 나는 어떻게 달라졌을까?

부트캠프를 진행하면서 총 4번의 협업 프로젝트를 통해 학습 기간 익혔던 기술들을 적용해볼 수 있었습니다. 부트캠프 이전과 달리 프로젝트마다 적용하게 되었거나 프로젝트를 통해 새로 얻게 된 지식들이 많은 것 같아요. 또 협업 프로젝트로 인해 다른 사람들과 어떻게 함께 개발해야 하는지를 알게 된 것이 너무 좋았습니다.

1) 웹표준 및 웹접근성 적용

저는 패스트캠퍼스 X 야놀자 부트캠프에서 마크업 언어를 배우면서 특히나 웹표준과 웹접근성을 중요하게 생각했던 것 같아요. 어떻게 하면 시멘틱한 마크업을 할 수 있을까, 또 어떻게 해야 모든 사람에게 정보를 공유할 수 있을까 고민했습니다.

웹표준을 지키기 위해 시멘틱한 마크업을 하려고 했고, 그러기 위해서는 각 태그별 특징을 명확히 인지할 필요가 있었습니다. 특히 각 태그의 특징을 아는 것은 웹 접근성 확보를 위해서도 꼭 필요한 부분이었기 때문에 단순한 기능 구현보다는 의미있는 코드를 작성하려고 노력하게 되었어요.

예를들어 ol과 ul의 차이점에서 스크린리더가 어떻게 읽어주는지를 고려한다면 같은 리스트 태그라도 어떤 것을 쓰는게 맞을지 한번 더 고민했습니다. 또 스크린리더 사용자를 고려하는 코드들을 프로젝트마다 적용하게 되었어요. 특히나 각 섹션별 설명을 p태그로 넣어두고 디자인 요소를 해치지 않기 위해 accessibility hidden 스타일을 적용하여 읽어주기만 한다던가 반대로 디자인적으로만 사용되는 이미지 요소이기에 스크린리더가 읽어줄 필요가 없는 상황에서는 aria-hidden 속성을 적용하게 되었습니다.

2) 다양한 스타일 라이브러리와 프레임워크 경험

또 다양한 스타일 라이브러리와 프레임워크를 경험하면서 “어떨 때 어떤 것을 써야겠다”하는 자신만의 기준도 생기게 되었습니다. 예를 들어 생산성이 중요한 프로젝트에서는 tailwindCSS와 같은 유틸리티 퍼스트 스타일 프레임워크를 통해 빠른 개발을 할 수 있었고, 충분한 기획과 디자인 시스템 아래에서 시작된 개발에서는 Sass를 이용하여 다양한 함수와 mixin을 통해 디자인 시스템에 맞춰 세팅을 해두고 개발에 들어간다면 훨씬 수월하게 개발이 진행된다는 것을 경험했어요.

또 차크라 ui와 MUI와 같은 라이브러리를 통해 보다 쉽게 ui 컴포넌트를 구현해볼 수 있었습니다. 다만 ui 컴포넌트 라이브러리의 경우 커스터마이징이 오히려 생개발보다 힘든 경우도 있었기 때문에 기획의 디자인이 라이브러리 테마와 거리가 멀다면 오히려 적용하지 않거나 headless ui를 적용하는 것이 좋겠다는 생각을 했습니다.

3) 다양한 협업 툴과 git 전략 이용 경험

총 4번의 협업프로젝트를 진행했습니다. 디자인, 기획, 백엔드, 프론트엔드가 함께 프로젝트를 경험하게 되면서 실제 기업에서 어떻게 함께 일하게 될지를 미리 경험해본 느낌이었어요. 또 다양한 협업 툴을 통해 일정을 관리하고 소통하면서 커뮤니케이션 능력을 익혔구요.

또 프로젝트마다 다양한 프로젝트 관리 전략을 적용해보면서 협업을 경험할 수 있었습니다. jira나 github project 경험, 그리고 gitflow와 같은 브랜치 관리 전략 등을 적용해보면서 이제 실무에 들어가서도 다른 사람과 함께 협업할 능력을 갖추었다는 자신감을 가질 수 있게 되었습니다.

4) 스스로 학습하는 방법을 익힘

패스트캠퍼스 X 야놀자 부트캠프는 온라인으로 진행되었습니다. 제공된 강의를 통해 정해진 커리큘럼을 따라가면서 자기가 원하는 공부를 추가적으로 진행할 수 있었던 점이 저에게는 정말 큰 성장의 발판이 되었던 것 같아요.

개발을 공부하다보면 배운 지식들에서 새로운 가지들이 자꾸 나오는 것을 경험했습니다. 이것들을 ‘앨리스의 토끼굴’이라고 표현하더라구요. 그래서 그런 지식들까지 다 공부해서 섭렵해버리겠다! 라는 생각을 제 모토로 삼았습니다. 그래서 제 프로필 소개가 ‘토끼굴을 정복하는 개발자’에요 ㅎㅎ.

강의를 통해 html5를 배우면서 웹 접근성이라는 개념을 알게 되었고, 이렇게 연결된 지식들을 더 공부하기위해 WCAG2.1을 공부하면서 더 깊은 이해가 가능했어요. 만약 다른 커리큘럼 진행만을 강요하는 부트캠프였다면 이런식의 공부가 힘들었을 것 같아요.

5) 유저 뿐만이 아니라 기업의 입장에서도 코드를 작성할 수 있게 됨

프론트엔드 개발자가 유저 중심의 코드를 작성해야 한다는 것은 모두가 알고 계실 내용일 것 같아요. 그런데 기업의 입장에서도 고민을 해보는 것이 필요하다고 생각합니다. 여러 프로젝트를 진행하고 또 배포와 관련하여 여러 비용 처리를 경험하게 되면서 기업의 입장에서 코드를 작성하는 것이 필요한 일이라는 것을 깨닫게 되었습니다.

예를들어 서버 비용을 줄이기 위해 서버에 fileList를 그대로 전달하는 것이 아닌 S3의 Presigned URL을 이용하여 따로 업로드하고, 그 url만 서버에 전달해주어 서버 비용을 낮출 수 있었어요.

부트캠프 수료!!!!!

드디어 부트캠프를 수료하게 되었네요. 7개월이 짧은 시간은 아니지만 저에게는 정말 순식간에 지나간 것 같습니다. 그만큼 부트캠프에 몰입하고 있었던 것 같아요. 좋은 사람들을 많이 만날 수 있었고, 함께 성장할 수 있었어요. 이런 기회를 준 패스트캠퍼스에 정말 감사하는 마음 뿐이랍니다.

내가 최우수 수료생!!

너무나 감사하게도 이번 부트캠프 최우수 수료생으로 선정되었습니다. 과정 진행 동안의 프로젝트와 과제, 퀴즈 등을 종합하여 선정되었는데, 정말 수강생 분들끼리의 점수 차이가 미미하더라구요. 운이 좋았다는 생각과 동시에 그만큼 열심히 한 나 자신을 칭찬해주고 싶었습니다. 최우수 수료생에게는 모의 면접과 모의 코딩테스트 등 여러가지 취업을 위한 추가적인 혜택이 있었어요. 잘 활용해서 꼭 원하는 곳에 취업하도록 노력하겠습니다. 그 동안 감사했습니다!

돌아온 패스트캠퍼스 X 야놀자 부트캠프 포스팅입니다! 2024년의 새로운 해의 시작과 함께 1월부터 저희 부트캠프에서는 ‘파이널 프로젝트’를 진행했습니다. 이번 프로젝트에서는 기획, 디자인, 백엔드, 프론트 각 포지션들이 하나의 팀을 이루어 진행하게 되었습니다. 저번 미니 프로젝트에서는 백엔드와 프론트 두 포지션만이 있었기 때문에 개발 외적으로도 디자인이나 앱 기획 등을 신경쓸게 많았는데요! 이번 파이널 프로젝트에서는 기획분들의 요청에 맞추어 구현만 하면되었기 때문에 개발에만 집중할 수 있어서 정말 좋았던 것 같아요.

기업 연계 프로젝트

이번 프로젝트는 야놀자의 기업 RFP를 제안서를 토대로 개발이 이루어졌습니다. 패스트 캠퍼스 X 야놀자 부트캠프에서만 경험할 수 있는 최고의 프로젝트라고 생각했어요. 실제 기업의 문제 및 니즈를 분석하고 스스로 해결책을 개발하는 과정을 경험할 수 있었습니다. 개발 중간 중간 야놀자 실무진분들의 멘토링을 받을 수 있었고, 주마다 꾸준하게 진행사항들을 확인하고 그에 대한 의문점이나 조언들을 멘토님에게 얻을 수 있었습니다.

팀 결성!

저희 팀은 ‘여행 여정 공유 플랫폼’을 주제로 개발을 진행하게 되었습니다. 팀 구성은 디자이너 1명, PM 4명, 프론트엔드 6명, 백엔드 5명으로 이루어지게 되었습니다. 미니에 비해 규모가 확 커졌다는 것이 느껴졌어요. 기획분들과 디자이너분이 추가된만큼 피그마도 엄청 무섭게 커졌습니다. 처음엔 와 우리가 저걸 개발할 수 있을까? 싶을정도로요..

프로젝트 기획과 디자인은 피그마를 통해 진행되었습니다. 디자이너와 기획분들과는 피그마 내의 댓글을 통해 기능들을 구체화하고 완성시켰어요. 각자 작업하면서 의문이 드는 부분이나 수정이 필요하다고 생각되는 부분들은 댓글로 달아놓아 확인할 수 있게 하였고, 구체적인 상의는 매일 있는 코어 타임과 주 한번의 팀 미팅을 통해 방향을 정했습니다.

디자인의 경우 전체적인 앱의 통일성을 위해 글꼴과 크기, 볼드값을 사용될 컴포넌트별로 정리해주셨고, 앱 전반적으로 사용될 토스트나 알림모달, 그리고 박스들의 형태또한 정해서 전해주셨습니다. 이번 프로젝트에서 저희는 sass를 사용하기 때문에 미리 저렇게 정리해서 주시니 변수들과 함수에 적용시켜 개발을 정말 편하게 할 수 있었어요.

와이어프레임

https://www.figma.com/file/oeQ2vOcyO3SuInuMf3Dlj8/%EC%B5%9C%EC%A2%85-%ED%99%94%EB%A9%B4(PM)?type=design&node-id=0-1&mode=design&t=nZD111AWFn7ozFJx-0


확실히 디자인, 기획 분들과 함께하는 프로젝트다보니 피그마 규모 부터가 저번 프로젝트랑 다릅니다…ㅠ (내가 할 수 있을까 걱정도 많이 했어요)

디자인가이드

https://www.figma.com/file/ypTLv92s72sihUApnxjP5C/%EA%B0%95%EC%9E%90%EB%B0%AD-%ED%94%BC%EA%B7%B8%EB%A7%88?type=design&node-id=40-2&mode=design&t=Jf8iEjAoqYVNtYJX-0

디자인 분께서 각 컴포넌트별로 세세하고 통일감있게 작성해주셨어요. 덕분에 미리 sass 변수와 mixin에 등록하고 뽑아쓰면서 편하게 개발에 집중할 수 있었습니다!

개발 진행

Sass 변수 및 mixin 적용

디자이너분의 디자인 가이드를 바탕으로 sass 변수에 색상과 글씨를 선언해주었습니다. 특히 font size, font weight, line height의 경우 sass 맵을 생성하고 mixin 내에서 불러와 적용할 수 있도록 하였습니다.

실제 스타일이 적용되는 부분에서는 @include typography(headline) 처럼 작성해주면 디자이너분이 headline에 적용되길 기대한 글씨에 대한 모든 값들이 한번에 적용되었습니다!! 확실히 이전 프로젝트들과 달리 디자이너분이 디자인 시스템을 갖추어주시니 개발이 너무 편해졌어요.

MSW 적용

프론트엔드의 가장 이상적인 개발 진행은 백엔드 API가 완성되어 거기에 맞춰 데이터 페칭을 하고 그에따른 UI를 화면에 출력하는 것이라고 생각합니다. 하지만… 같이 시작한 프로젝트에서 저희가 백엔드의 완성만을 기다릴 수는 없어요. 그래서 MSW를 적용했습니다!

개발 환경에서는 MSW가 api 요청을 탈취해서 모킹 서버로부터 저희가 정한 응답을 받고 데이터들을 페이지에 렌더링할 수 있습니다. 이게 정말 좋았던게 백엔드와의 협의를 통해 엔드포인트만 정해둔다면 이후 배포시에도 코드의 변경없이 실제 서버로 그대로 요청을 보낼 수 있다는 것이 신세계였습니다.

프로젝트 막바지에는 위의 이미지처럼 모킹 함수 내에 return문을 추가시켜서 모킹하지 않고 바로 서버로 요청을 보내보면서 테스트 해볼 수 있었어요!

마치며

파이널 프로젝트도 끝나가고 이제는 수료도 코앞으로 다가왔습니다. 6개월 가량 정말 길거라고 생각한 것이 무색하게 후딱 지나가버렸어요. 패스트캠퍼스 X 야놀자 부트캠프 덕분에 스스로 자신감도 생기고 무엇보다 어떻게 개발자로서 발전해나갈지 깨닫는 계기가 된 것 같아요. 이번 마지막 프로젝트도 잘 마치고 다음 포스팅엔 마지막으로 부트캠프를 진행하며 느꼈던 총 후기로 돌아오겠습니다.

이번 포스팅은 패스트캠퍼스 X 야놀자 부트캠프에서 진행한 ‘미니 프로젝트’를 진행하며 겪은 경험과 과정, 그리고 느낀점들을 공유하려고 합니다. 처음으로 경험했던 백엔드와의 협업 프로젝트였는데요! 이번 프로젝트를 하면서 팀장 역할도 맡게 되어서 부담감도 있었던 프로젝트였던 것 같아요. 하지만 함께 노력해준 팀원들과 멘토님의 도움 덕분에 수월하게 진행된 것 같습니다. 패스트캠퍼스 X 야놀자 부트캠프의 미니프로젝트! 어떻게 진행이 되었나 함께 보실까요?

프로젝트의 시작

이번 프로젝트에서는 ‘숙박 예약 API 서비스’를 과제로 전달받았습니다. 프로젝트 RFP를 통해 프로젝트 정의서와 기능적 요구사항들을 전달받고 해당 기능들을 백엔드 팀과의 협업을 통해 구현해나가야 했어요. 11월20일 ~ 12월01일 2주간 정말 열심히 개발했답니다.

기획과 디자인

프로젝트의 기획은 피그마를 통해 구체화하였습니다. 팀 전체가 모여 와이어프레임과 세부 디자인을 결정하고 세부적으로 백엔드 팀원분들과의 상의를 통해 우선 순위 기능들, 구현하기 힘들거나 후순위인 기능들을 정리하였습니다.

Figma URL

기술 선택

기획이 완료되어 가면서 개발시 어떠한 기술들을 쓸 것인가에 대해 팀원들과 이야기를 나누었습니다. 제일 먼저 저희가 주목한 부분은 ‘어떤 기술을 써야 기업에 이익을 극대화할 수 있을 것인가?’였습니다. 숙박 예약 사이트의 각각의 숙박업소들이 나와있는 페이지와 객실 정보들을 확인하는 페이지들의 노출도가 높을 수록 우리 사이트를 접근하는 유저들이 많을 것이고, 그것이 기업의 이익과 연결된다고 생각했습니다. 따라서 이전 프로젝트와는 달리 NEXT.JS를 사용하기로 하였습니다.

NEXT.JS는 서버 컴포넌트가 있기 때문에 SSR이 가능합니다. 즉 자바스크립트 기반으로 빈 문서를 가져와 렌더링하는 리액트의 CSR과는 달리 우리가 서버 컴포넌트로 활용하고자 하는 페이지는 해당 정보를 문서에서 그려올 수 있기 때문에 검색 엔진 최적화에 유리합니다. 바로 우리 페이지를 더 쉽고, 많이 노출시킬 수 있다는 뜻이에요!

이후에는 백엔드에서 전달해준 API 명세등을 미리 타입으로 지정해두기 위해 Typescript도 도입했습니다. API 명세에 없는 키값을 입력하거나 타이핑 오타 등으로 일어날 오류를 런타임환경에서 확인하고 수정할 수 있었어요.

마지막으로 스타일 프레임워크는 TailwindCss를 선택했습니다. TailwindCss를 선택한 이유는 2주라는 짧은 시간 동안 처음 진행해보는 백엔드와의 협업 프로젝트였기 때문에 개발 생산성이 중요하다고 생각했기 때문입니다. 유틸리티 퍼스트 방식을 도입하면서 화면에 그려지는 레이아웃들을 직접 확인하면서 바로바로 적용해나갈 수 있어 확실히 빠른 개발 진행이 가능했습니다.

협업 방식과 개발 전략 설정

다음으로 정해야 할 것은 ‘어떻게 협업할 것인가?’였습니다.

ZEP

전체적인 회의 진행과 기본 커뮤니케이션은 ZEP을 통해 이루어졌습니다. ZEP은 화면공유, 음성채팅, 영상채팅 등이 가능한 협업 툴인데요, 마치 게임처럼 아기자기한 캐릭터들을 이동해가면서 커뮤니케이션을 할 수 있어서 너무 재미있었어요.

저희 프로젝트 방은 이렇게 생겼어요. 프론트 책상에서 회의를 하다가 백엔드 팀에게 질문사항이 생기면 ‘쫑쫑쫑’ 백엔드 테이블로 걸어가서 물어보곤 했답니다.

Trello


다음으로는 백엔드와 프론트엔드 사이에서 맡은 역할 별로 요구사항이나 수정사항, 또는 오류 등에 대해 1:1로 요청을 보내기에 trello가 편하다고 생각되어서 도입하게 되었습니다. 같은 프론트엔드 팀원이나 백엔드 팀원에게 요청을 보낼 때 간단하게 카드를 달아놓을 수 있습니다. 해당 주제에 대한 카드에서 댓글을 통해 서로 의견을 주고 받거나 진행사항을 공유할 수 있었습니다.

Github

마지막으로는 가장 주축이 되었던 모두의 Github입니다! 프론트엔드 팀의 개발에 있어서 프로세스를 정해두었어요.

  1. 분담한 역할에 따라 구현할 기능에 대한 개발 내용을 미리 이슈로 등록한다.
  2. 개인이 개발한 내용은 반드시 PR을 통해 팀원들과 공유하고 코드리뷰와 approval을 거친 뒤에 브랜치에 적용한다.

이 때 각 팀원 별로 브랜치를 분기해서 PR을 보냈어야 했는데 이를 단순화하기 위해 Gitflow도 도입했습니다! 저희가 사용한 브랜치 분기 전략은 밑의 링크에서 확인하실 수 있어요.

브랜치 분기 전략

프로젝트 진행

프로젝트는 2주 동안 정말 눈코 뜰 새 없이 빠르게 지나갔습니다. 그나마 다행인 점은 기획 단계에서 백엔드와의 상의를 통해 전체적인 그림을 잘 잡아놨기에, 큰 충돌 없이 잘 진행될 수 있었어요.

저는 인증관련 페이지들과 기능들을 맡아 구현하게 되었습니다.

이 때 NEXT.JS에서 쿠키 관련 이슈 때문에 너무 고생을 했는데… 이 부분은 차후에 NEXT.JS의 쿠키 사용에서의 이슈와 해결 방법이라는 주제로 다시 포스팅 하도록 할게요!

또 저는 팀장으로서 전체적으로 팀원들의 개발 진행 사항이나 오류들을 파악해야 했습니다. 코드 리뷰도 정말 열심히 봤구요. 저의 역량은 이 모든 것을 자연스럽게 할 수 있을 정도로 충분하지 않다고 생각했고, 처음에 팀장을 맡았을 때에도 너무 부담이었던 부분이었습니다. 그러나 우리는 팀!
혼자서 모든 것을 해결한 것이 아니었어요. 팀원들의 도움과 스스로의 노력이 더해졌을 때 프로젝트는 정말 순조롭게 흘러갈 수 있었습니다.

쌓여가는 Close PR과 우리들의 피, 땀 , 눈물…

프로젝트 완성과 리팩토링

드디어 2주가 지나고 저희 프로젝트가 완성되었습니다. 너무 힘든 2주였지만 완성된 결과물들이 제대로 동작하는 것들을 보면서 피로가 싹 날아가는 느낌이었어요! (물론 아직 부족한 점이 많아요…)

STAYINN 사이트 링크

OVERVIEW

시연영상

리팩토링

프로젝트가 끝난 뒤에는 2주간의 리팩토링 기간이 주어졌습니다. 각 팀원별로 맡은 부분에서 아쉬웠던 부분이나 추가적인 기능 구현들을 진행했습니다.

저의 경우에는 가장 주된 리팩토링 기능이 인가 여부에 따라 페이지를 라우팅하는 부분이였어요. 우리 사이트의 메인 페이지나 숙박/객실 정보들을 보여주는 페이지의 경우는 로그인이 없어도 이용할 수 있지만, 마이페이지, 장바구니 페이지, 예약 하기 등에서는 반드시 로그인이 필요하기 때문에 로그인 페이지로 리다이렉트 시켜주는 로직이 필요했거든요.

기존 프로젝트에서 제가 구현한 방식은 페이지가 렌더링 되면서 로그인한 유저 정보를 API 요청하고 로그인이 되어있지 않아 요청이 실패하면 리다이렉트 시켜주는 방식이었습니다. 문제는 이렇게 구현했을 경우에는 페이지가 렌더링 되면서 요청하기 때문에 잠시동안 해당 페이지가 보여지다가 로그인 페이지로 넘어가는 깜빡거림 현상이 있었습니다.

이를 해결하기 위해 해당 페이지로 이동할 때 렌더링 이전 유저 정보를 요청하고 그 결과에 따라 렌더링 여부를 결정하는 것이 필요했어요. 이를 위해 미들웨어를 작성했습니다.

이 때 미들웨어를 작성하면서 쿠키 이슈가 생겼는데, 백엔드 단에서 Set-cookie를 해주더라도 next서버에는 쿠키라는 개념이 없기 때문에 next서버에서의 요청에서 쿠키가 담겨가지 않아 인증 요청을 할 수가 없었습니다. 이를 해결하기 위해 로그인시 브라우저에 Set-cookie된 토큰들을 직접 next서버에 변수로 불러와 header의 cookie로 직접 넣어서 api 요청을 해주어야 했어요. ‘next-client-cookies/server’의 CookiesProvider를 RootLayout에 감싸주어 서버 컴포넌트와 클라이언트 컴포넌트에서 쿠키들을 공유할 수 있게 했습니다. 이후엔 Set-cookie한 토큰들의 값을 로그인시 읽어와 CookiesProvider에 따로 set 해주어 미들웨어에서 토큰들을 읽을 수 있게 하였고, 마찬가지로 로그아웃 시에는 따로 CookiesProvider의 토큰 값들을 remove 해주는 로직을 추가해주는 것으로 해결했습니다.

마치며

2주간의 협업 프로젝트는 저의 역량을 한층 더 끌어올려준 것을 넘어서 다른 사람들과의 협업이 어떻게 진행되고 그 안에서 제가 어떤 방식으로 다른 사람들에게 내 요구를 전할지, 어떻게 표현할지를 생각해보는 계기가 되었습니다. 어느덧 벌써 ‘파이널 프로젝트’를 앞두고 있는데요. 이 경험을 잘 살려서 파이널 프로젝트 또한 잘 마무리하고 찾아뵙겠습니다!

오늘 포스팅은 지금까지 약 4개월 과정동안 패스트캠퍼스 부트캠프 과정을 진행하면서 경험한 것들과 느낀점들을 공유하려고 합니다. 이젠 정말 과정의 반을 넘기게 되었는데요 회고를 작성하려고 지난 저의 발자취들을 따라가본 결과 정말 많은 성장을 했음을 알 수가 있었어요! 제 성장의 토대가 되어주신 강사님들과 멘토님, 그리고 운영진 분들께 모두 감싸의 말씀 올립니다.

1. HTML/CSS

HTML/CSS는 사실 부트캠프 과정을 시작하기 전에도 학습을 했던 부분이었기에 이번 과정에서는 보다 심화된 내용들을 학습하고 싶었습니다. 단순한 마크업 구현에서 벗어나 접근성과 웹표준, 그리고 최적화 관점에서 양질의 코드를 쓸 수 있도록 공부해야겠다는 생각을 했습니다.

HTML/CSS과제는 원하는 사이트를 하나 선정해서 클론 코딩을 하는 것이었습니다. 앞서 말한 여러 추가적인 공부 내용들을 적용해보고자 노력했습니다.

The Armor Bearer 클론

깃허브 링크: https://github.com/NamgungJongMin/Yanolja_FE_HTML-CSS

이 프로젝트를 하면서 Scss의 활용을 좀 더 심화해서 익힐 수 있었습니다. BEM 방법론을 적용하면서 중첩규칙을 보다 효율적으로 이용할 수 있었고, Mixin 등의 유틸함수를 통해 유지보수면에서도 더 나은 코드가 무엇일지 고민해볼 수 있는 시간이었습니다.

또한 스크린리더 사용자의 긍정적 경험을 위해 디자인적 요소라 읽어주지 않을 요소와 읽어주진 않지만 어떤 섹션인지 설명해주어야 할 것 같은 부분에 읽어주도록 처리해주었습니다.

추가로 렌더링 관점에서 웹성능을 끌어올릴 수 있는 방법에 대해 고민했습니다. 이미지 최적화와 스타일 적용 방식의 최적화를 고민하고 적용해보면서 매우 값진 시간을 보낼 수 있었습니다.

2. Javascript

자바스크립트의 경우에는 프로젝트를 생성하고 진행하면서 어떤 폴더 구조를 사용할지, 같은 기능이라도 어떤 방식이 유지보수에 도움이 될지에 대한 고민을 많이 했던 것 같습니다.

직원 관리 서비스

깃허브 링크: https://github.com/NamgungJongMin/Yanolja_FE_JavaScript



로그인/회원가입 기능을 구현하면서 한 스크립트 파일에 담는 것이 아니라 스키마로 분류하고 validation 결과를 반환하는 getter함수를 지니는 객체를 만들어 파일을 분리해주었습니다. 단순히 어떻게 구현할지가 아니라 어떤 방법으로 구현해 나갈지 고민할 수 있는 뜻 깊은 시간이었어요.

3. 토이프로젝트 1

다음으로 진행된 토이프로젝트는 팀과 함께하는 협업 프로젝트였습니다. 제가 무려… 팀장을 맡았답니다. 단순히 혼자 하는 것에 비해 서로에 맞추고 또 일정을 조율해나가면서 전체적인 코드 컨벤션도 맞춰야 했습니다. 또한 커밋 방식이나 브랜치 분기 전략 등을 도입해보면서 실제 현업에서 일하는 개발자 분들의 입장을 쪼..곰? 이나마 체험해볼 수 있었습니다.

직원 위키 사이트

깃허브 링크: https://github.com/NamgungJongMin/toy1


처음으로 파이어베이스를 사용해보면서 데이터들을 실제 저장하고 가져오는 것들을 해볼 수 있었습니다. 너무 흥미로웠어요. 협업 프로젝트는 단순히 혼자하는 프로젝트와는 정말 달랐습니다. 팀원들에게 모르는 것들을 질문하고 또 내가 질문 받았을 때 해결해주면서 뿌듯함을 느꼈고, 함께 무언가를 만들어 나간다는 고양감을 느낄 수 있었던 것 같습니다. 실제 현업에 가서도 다른 사람들과 일을 하게 될텐데 앞서 경험한 팀 프로젝트는 큰 힘이 되어줄 것이라고 생각합니다.

마치며

어떠신가요?! 패스트캠퍼스 부트캠프를 통해 점점 더 고민하고, 노력하고, 성장해나가는 제 모습이 보이셨나요? 오늘 회고 포스팅은 단순히 글을 쓰는 것을 넘어 지금까지의 저를 되돌아보고 반성하고, 잘했다 기특하게 생각할 수 있었습니다. 여러분들도 지금 고민하고 있다면 도전하세요. 당장은 미약하더라도 노력하는 시간은 절대 배신하지 않을거에요. 오늘 포스팅도 읽어주셔서 너무 감사했습니다.

0%