[Fastlane] Fastlane을 통해 빌드를 자동화하자 1편

[Fastlane] Fastlane을 통해 빌드를 자동화하자 1편

이채현

노션으로 보기

What is the Fastlane?

  • ruby 코드로 만들어진 앱 배포 자동화 툴
  • 커맨드라인으로 빌드 할 수 있음
  • CI 시스템과의 통합 용이
  • 여러 구성원이 App Store에 앱을 배포하거나 단일 장치에 앱을 설치할 때 별도의 코드 서명 ID가 필요하지만, fastlane의 match를 사용하여 하나의 중앙저장소를 만들 수 있음
    • 기존 배포 인증서는 팀원 각각 만들어야 하고, 만료 기한이 있었던 것과 달리 깃 저장소에 저장된 배포 인증서로 사용 가능
    • 단, 이중인증 필요하다는 한계 존재

Run Fastlane

01. setting

fastlane 설치 및 세팅

#1. Install the latest Xcode command line tools

1
xcode-select --install

#2. ruby 2.5.0이상 버전 다운로드 필요

  • Homebrew를 이용한 설치

  • macOS Sierra 기준으로, 맥에는 이미 ruby가 설치되어 있음

  • 기본설치 되어 있는 Ruby가 오래된 버전이라면 rbenv를 다운로드 하여 버전관리를 하는 것이 좋음

    1
    brew install rbenv ruby-build
  • rbenv 초기화

  • rbenv의 설치가 완료되었다면 아래에 명령어를 실행하여 rbenv을 초기화한다.

  • 아래 명령어를 실행하면 다음과 같이 나온다

    1
    2
    3
    4
    5
    rbenv init

    ---
    # Load rbenv automatically by appending
    # the following to ~/.bashrc_profile:*eval "$(rbenv init -)"`
  • 위에 설명과 같이 .bashrc_profile 파일을 열고 아래와 같이 수정한다.

    1
    2
    3
    4
    5
    eval "$(rbenv init -)" 또는
    eval "$(rbenv init - zsh)"

    # 그리고 아래 줄을 작성한다.
    export PATH=$HOME/bin:/usr/local/bin:$PATH
    1
    source ~/.zshrc
  • 루비 2.5.0 이상 설치

    1
    2
    3
    4
    5
    rbenv install 3.1.2

    rbenv global 3.1.2 rbenv rehash

    ruby -v

#3. fastlane을 설치하는 두가지 방법

02. Init Fastlane

fastlane을 위해 사용할 폴더 구조는 다양하다. 필자는 RN 프로젝트에서 다음과 같이 사용하고 있음.

#1. 프로젝트 최상위 루트에서 init 후 아래와 같은 폴더 구조 생성

1
2
3
bundle exec fastlane init

이후 4번 선택

Untitled

Untitled

root project |_ fastlane |_ android |Fastfile | ios |Fastfile | android |_ ios

#2. Fastfile설정

1
2
3
4
5
6
7
fastlane_require 'dotenv'

before_all do
end

import("./ios/Fastfile")
import("./android/Fastfile")

#3. env 설정

  • Android와 iOS의 빌드를 위해 env를 분리시킨다.

    • 프로젝트 최상위 루트에 .env.FLAndroid, .env.FLIos 파일을 생성한다.

    env파일

    • .env.FAndroid
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # 개발

    APPCENTER_API_TOKEN={앱센터 토큰}
    APPCENTER_OWNER_NAME={"앱센터 사용자 이름"}
    APPCENTER_APP_NAME={"앱센터 앱 이름"}
    APPCENTER_DISTRIBUTE_APK="./android/app/build/outputs/apk/development/release/app-development-release.apk"

    # 상용

    JSON_KEY={"플레이스토어 API 액세스 키"}
    STORE_DISTRIBUTE_AAB="./android/app/build/outputs/bundle/productionRelease/app-production-release.aab"
    PACKAGE_NAME="앱 패키지 이름"
    • .env.FLIos
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    IOS_PROJECT_PATH="./ios/project.xcodeproj"

    SIGNING_IDENTITY_DEBUG="Apple Development: "
    SIGNING_IDENTITY_RELEASE="Apple Distribution: "

    # 개발
    MATCH_ENV_PREFIX_DEVELOPMENT="sigh_io.project.dev_development"
    MATCH_ENV_PREFIX_DEVELOPMENT_EXT="sigh_io.project.dev.OneSignalNotificationServiceExtension-Dev_development"

    MATCH_ENV_PREFIX_ADHOC="sigh_io.project.dev_adhoc"
    MATCH_ENV_PREFIX_ADHOC_EXT="sigh_io.project.dev.OneSignalNotificationServiceExtension-Dev_adhoc"

    APPCENTER_API_TOKEN=앱센터 토큰
    APPCENTER_OWNER_NAME="앱센터 사용자 이름"
    APPCENTER_APP_NAME="앱센터 앱 이름"
    APPCENTER_DISTRIBUTE_IPA="./projectDev.ipa"

    # 상용
    MATCH_ENV_PREFIX_APPSTORE="sigh_io..project.app_appstore"
    MATCH_ENV_PREFIX_APPSTORE_EXT="sigh_io.project.app.OneSignalNotificationServiceExtension-Prod_appstore"

    STORE_DISTRIBUTE_IPA="./project.ipa"

#4. env 로드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
platform :android do
before_all do
Dotenv.overload '../.env.FLAndroid'
...
end
...
en

----------------------------------------------

platform :ios do
before_all do
Dotenv.overload '../.env.FLIos'
...
end
...
en

#5. Appfile 셋팅 (환경변수 파일)

  • 필요에 따라 작성하면 된다.
1
2
3
4
5
# app_identifier('io.project') # The bundle identifier of your app
apple_id('[email protected]') # Your Apple email address

# itc_team_id('####') # App Store Connect Team ID
# team_id("ABCDE") # Developer Portal Team ID

03. How to Run Fastlane

local에 설치 된 ruby 버전이 아닌 프로젝트에서 지정한 ruby버전을 사용하기 위해 bundle exec 명령어를 이용

1
2
bundle update --bunder
bundle exec fastane ....

Install Plugin

1
2
3
4
5
bundle exec fastlane add_plugin appcenter
bundle exec fastlane add_plugin load_json
bundle exec fastlane add_plugin yarn

# ... 등등 필요한 플러그인을 추가한다.
  • add plugin 이후, fastlane 폴더 내 Pluginfile이 생성

    1
    2
    3
    4
    5
    6
    7
    # Autogenerated by fastlane
    #
    # Ensure this file is checked in to source control!

    gem 'fastlane-plugin-appcenter'
    gem 'fastlane-plugin-load_json'
    gem 'fastlane-plugin-yarn'
  • Pluginfile 생성 체크 후, 프로젝트 루트 디렉토리에 Gemfile 내에 생성된 경로와 일치한지 확인하기

    1
    2
    3
    4
    ...

    plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
    eval_gemfile(plugins_path) if File.exist?(plugins_path)
  • 마무리

    1
    2
    bundle install
    # 이후, Gemfile.lock에 추가한 플러그인이 생겼는지 확인
[JS] 클로저 간단 정리

[JS] 클로저 간단 정리

이채현

  • 클로저는 먼저 자바스크립트 변수의 유효범위를 이해해야한다.
  • 클로저를 명확히 무엇이다라고 말하기는 어렵다.
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
function returnX(){
return 'x';
}

const x = returnX()
console.log(typeof x) // string

----------------------------------------------

function returnX() {
let x = 'x';
return function returnY() {
return x + 'y';
}
}

const x = returnX()
console.log(typeof x) // function => return 값이 함수 덩어리이기 때문에

const x = returnX()();
console.log(typeof x) // string
function sum(num1) {
return function (num2) {
return num1 + num2;
};
}

const sum5 = sum(5); // 숫자5가 계속 바인딩되어 있는 상태
console.log(sum5(10)); // 15

은닉화

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 privateData() {
let temp = 'a';
return temp;
}

const result = privateData(); // privateData를 실행시켜야만 temp값을 알 수 있다.
console.log(result);

-------------------------------------

function privateData() {
let temp = 'a';
return {
value: () => {
return temp;
},
changeValue: (newVal) => {
temp = newVal;
}
};
}

const private = privateData();
console.log(private.value()); // a
private.changeValue('b');
console.log(private.value()); // b

활용사례

  • 고민해봤을 때 debounce와 throttle에서 …사용

    • debounce: 어떤 이벤트를 실행할 때 과하게 실행되는 것을 지연시켜주는 역할 (클릭지연..무한스크롤 지연)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    buttonElement.addEventListener(
    'click',
    debounce(handleClick, 500),
    )

    function debounce(func, timeout = 300) {
    let timer;

    return (...args) => {
    clearTimeout(timer);

    timer = setTimeout(() => {
    func.apply(this, args);
    })
    }
    }
[JS] Class 간단 정리

[JS] Class 간단 정리

이채현

클래스

  • class 선언은 프로토타입 기반 상속을 사용
  • 정의: 함수 정의방법과 동일하게 가능, 함수 표현식과 함수 선언을 class표현식에서 사용 가능
1
2
3
4
5
6
7
8
9
10
11
function Person(name, age) {
this.name = name;
this.age = age;
}

class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
}

인스턴스

  • 싱글리터럴로 만든 객체도 각자의 인스턴스를 뜻함..객체이지만 인스턴스
  • 생성자 함수와 클래스를 활용하여…new 연산자와 더불어 만듬
1
2
3
4
5
6
function Func() {} // 생성자함수의 이름은 Pascal case로..

class Class {}

const newInstance = new Func();
const newInstance2 = new Class();

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
function Person(name, age) {
this.name = name;
this.age = age;
this.location = location;
}

Person.prototype.getName = function () {
return this.name + ' 입니다.';
}

=============================================

class Person {
constructor(name, age) {
this.name = name;
this.age = age;
this.location = location;
}

getName() {
return this.name + ' 입니다.';
}
}

const one = new Person('one', 1, 'Korea');
const two = new Person('two', 2, 'Korea');

console.log(one.getName()); // one 입니다.
  • 클래스는 뿐만 아니라 Private키워드, 정적메서드 등 수 많은 기능을 제공

클래스 확장 (extends, 상속)

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
// Super Class
class Animal {
constructor(name, sound) {
this.name = name
this.sound = sound;
}

getInfo() {
return this.name + '가(이)' + this.sound + '소리를 낸다.';
}
}
// Sub Class
class Friends extends Animal {
constructor(name, sound) {
super(name, sound); // 부모의 생성자함수를 호출가능
}
}

const dog = new Friends('개', '멍멍');
const cat = new Friends('고양이', '냐옹');

console.log(dog.getInfo()); // 개가(이)멍멍소리를 낸다.
console.log(cat.getInfo()); // 고양이가(이)냐옹소리를 낸다.

-------------------------------------------------

dog.contructor.name // Friends
cat.contructor.name // Friends

dog instanceof Friends // true
dog instanceof Animal // true
[JS] 프로토타입 간단 정리

[JS] 프로토타입 간단 정리

이채현

자바스크립트는 프로토타입 기반의 언어다.

constructor (생성자)

1
2
3
4
5
6
7
8
9
10
function Person(name, age) {
this.name = name;
this.age = age;
}

class Paerson {
constructor() {

}
}

생성자함수는 프로토타입이 모든 자바스크립트에 들어있듯이 생성자도 모든 자바스크립트에서 확인가능하다.

1
2
3
4
5
const one = new Person('one', 1);
const two = new Person('two', 2);

console.log(one.constructor); // [Function: Person]
console.log(two.constructor.name); // Person

어떠한 생성자로부터 생성되었는지 유추할 수 있다.

instanceof로 식별

object instanceof constructor

proto

브라우저에서 비표준으로 제공했던…

1
array.__proto__ ...로 array의 프로토타입에 접근

자바스크립트의 프로토타입에 직접 접근이 아니라 접근제어자로 접근할 수 있도록 도와주는 것으로 생각하면 됨. ⇒ 하지만 사용하지 않는 것이 좋음

ECMA Script2015부터는 표준화 된

  • getPrototypeOf()
  • setPrototypeOf()

를 사용하는 것을 추천

프로토타입 체인

1
2
3
4
5
6
7
8
9
const animal = {
sayName() {
return 'ANIMAL';
}
}

console.log(animal.sayName()); // ANIMAL
const dog = Object.create(animal);
console.log(dog.sayName()); // ANIMAL

프로토타입 확장 (extends, 상속)

부모 ⇒ 자식 … 상속보단 확장이란 개념이 더 이해하기 쉽다. 부모가 가진 기능보다 자식이 더 많이 가질 수 있기 때문

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
// Super Class
function Animal(name, sound) {
this.name = name
this.sound = sound;
}

Animal.prototype.getInfo = function () {
return this.name + '가(이)' + this.sound + '소리를 낸다.';
}

// Sub Class
function Friends(name, sound) {
Animal.call(this, name, sound); // 명시적 바인딩 -> Animal함수의 this를 Friends로 바인딩
}

Friends.prototype = Object.create(Animal.prototype);
Friends.prototype.constructor = Friends;

const dog = new Friends('개', '멍멍');
const cat = new Friends('고양이', '냐옹');

console.log(dog.getInfo()); // 개가(이)멍멍소리를 낸다.
console.log(cat.getInfo()); // 고양이가(이)냐옹소리를 낸다.

-------

dog.contructor.name // Friends

dog instanceof Friends // true
dog instanceof Animal // true
[JS] this 간단 정리

[JS] this 간단 정리

이채현

JavaScript에서 함수의 this 키워드는 다른 언어와 조금 다르게 동작한다.

엄격모드와 비엄격 모드에서도 일부 차이가 있다.

  • this는 scope와 관계가 있다.
  • 객체에도 영향을 준다.
  • this가 어디에 묶여있냐를 아는 것이 코드를 작성하는 시점, 동작하는 시점에 차이가 있을 수 있다.

(함수를 어떻게 호출했는지 상관하지 않고 this값을 설정할 수 있는 bind메서드를 도입했고, ES2015는 스스로의 this 바인딩을 제공하지 않는 화살표 함수 를 추가했습니다.)

암시적 바인딩

암시적인 this 바인딩.. 사용자가 생각하지 않은대로 동작할 수 있다.

전역 공간에서의 this

  • node.js 환경에서의 this는 global
  • 브라우저에서의 this는 window

함수

  • 함수에서의 this는 window ⇒ 전역공간에서의 this와 다르지 않다.

객체 (메서드)

  • this는 호출되는 대상의 객체를 가리키고 있다.
1
2
3
4
5
6
7
8
const obj = {
name: 'obj',
method: function() {
return this.name
}
}

obj.method() // 'obj'

명시적 바인딩

  • call(), bind(), apply()
1
2
3
4
5
6
7
8
9
10
11
12
13
const person = {
name: 'Lee',
sayName: function () {
return this.name + '입니다';
}
}

const zero = {
name: '베이스',
sayName: function () {
return this.name + '입니다';
}
}
  • call()
1
2
3
4
5
function sayFullName(firstName) {
return firstName + this.sayName()
}
const result = sayFullName.call(person, 'Chaehyeon ');
// Chaehyeon Lee입니다.
  • apply()
1
2
3
4
5
6
function sayFullName(firstName) {
return arguments[1] + this.sayName()
}

const result = sayFullName.apply(person, ['Chaehyeon ','채현 ']);
// 채현 Lee입니다.
  • bind()
    • this를 묶어서 사용가능한다.

    • React Class Component에서 사용했다.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      function sayFullName(firstName) {
      return firstName + this.sayName()
      }

      const sayFullNamePerson = sayFullName.bind(person);
      const sayFullNameZero = sayFullName.bind(zero);
      console.log(sayFullNamePerson('Chaehyeon'));

      // ChaehyeonLee입니다
[JS] 스코프 간단 정리

[JS] 스코프 간단 정리

이채현

전역 스코프 (Global)

  • 그냥 가장 바깥쪽
  • 언제 어디서나 접근 가능하기 때문에 재정의가 가능하다 ← 큰 문제
    • 그 결과, 프로그램의 실행 결과 때 내가 만든 변수들의 결과를 예측하기 어려워짐 → 팀 단위로 개발할 때 최악

지역 스코프

함수 스코프

블록 스코프

1
2
3
4
5
6
7
8
9
{
// 블록
}

function func() { // 함수 스코프 블록
}

if (true) { // 블록 스코프 블록
}

전역 객체

브라우저

⇒ window

NodeJS

⇒ Global

// this

  • 브라우저에서 호출하면 window불러옴

호이스팅 (Hoisting)

1
2
3
4
5
6
7
8
9
function foo() {
console.log(hoist); // undefined

var hoist = '호이스팅';

console.log(hoist); // 호이스팅
}

foo();
  • 첫번 째 console.log가 오류나지 않는 이유는

    • 변수 선언을 직접적으로 끌어올림
    • 그래서 위 코드와 아래 코드랑 같다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    function foo() {
    var hoist; // 1. 변수 선언
    console.log(hoist); // undefined // 2. 실행

    hoist = '호이스팅'; // 3. 재할당
    console.log(hoist); // 호이스팅
    }

    foo();

애초부터 var를 안쓰면 된다.

1
2
3
4
5
6
7
8
9
function foo() {
console.log(hoist); // Cannnot access 'hoist' before initialization

let/const hoist = '호이스팅';

console.log(hoist); // 호이스팅
}

foo(); // Cannnot access 'hoist' before initialization
  • 임시적 사각지대 (TDZ)가 생긴다.
  • 내부적으로 호이스팅이 일어나지만, 사용자에게는 보이지 않는것처럼 에러를 준다.

IIFE (즉시 실행 함수 표현)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function func() {

}

func();

==>

(function () {

})();

(function (num) {
console.log(num); // 1
})(1);

함수를 괄호안에 넣고 바로 실행

  • 함수 공간을 완전히 분리할 수 있기 때문에 사용한다.
    • 그래서 let/const가 없을 때에 사람들이 외부에서 접근할 수 없게 블록스코프를 흉내내게 사용했다.
[JS] 자료 다루기

[JS] 자료 다루기

이채현

객체

Object.keys()

  • 객체의 키들이다~~라는 느낌으로 객체의 키들이 배열로 반환된다.
1
2
3
4
5
6
7
8
const object1 = {
a: 'somestring',
b: 42,
c: false
};

console.log(Object.keys(object1));
// expected output: Array ["a", "b", "c"]

Object.values()

  • 객체의 값들이다~~라는 느낌으로 객체의 Value들이 배열로 반환된다.
1
2
3
4
5
6
7
8
const object1 = {
a: 'somestring',
b: 42,
c: false
};

console.log(Object.values(object1));
// expected output: Array ["somestring", 42, false]

Object.entries()

  • 객체를 인자로 받아, key와 value를 쌍으로 가진 배열을 반환한다.
  • for…in와 같은 순서로 주어진 객체 자체의 enumerable 속성을 가짐
    • 인덱스마다 배열을 가져서, 첫번째가 key, 두번째가 value이다.
      • 배열안에 배열
1
2
3
4
5
6
7
8
9
10
11
12
const object1 = {
a: 'somestring',
b: 42
};

for (const [key, value] of Object.entries(object1)) {
console.log(`${key}: ${value}`);
}

// expected output:
// "a: somestring"
// "b: 42"

배열

요소 추가와 제거

  • unshift ⇒ 배열의 앞에 요소 추가

  • push ⇒ 배열의 끝에 요소 추가

  • shift ⇒ 배열의 앞에 요소 제거

  • pop ⇒ 배열의 끝에 요소 제거

  • splice ⇒ 배열의 인덱스를 기반으로 요소 추가 및 제거

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    array.splice(start[, deleteCount[, item1[, item2[, ...]]]])

    const months = ['Jan', 'March', 'April', 'June'];
    months.splice(1, 0, 'Feb');
    // inserts at index 1
    console.log(months);
    // expected output: Array ["Jan", "Feb", "March", "April", "June"]

    months.splice(4, 1, 'May');
    // replaces 1 element at index 4
    console.log(months);
    // expected output: Array ["Jan", "Feb", "March", "April", "May"]

요소 병합

  • concat
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const array1 = ['a', 'b', 'c'];
const array2 = ['d', 'e', 'f'];
const array3 = array1.concat(array2);

console.log(array3);
// expected output: Array ["a", "b", "c", "d", "e", "f"]

const alpha = ['a', 'b', 'c'];

alpha.concat(1, [2, 3]);
// 결과: ['a', 'b', 'c', 1, 2, 3]

--------

const newArr = [...array1, ...array2];
// ['a', 'b', 'c', 'd', 'e', 'f']

고차 함수로 조작 (내장 메서드)

  • 대표적으로 map, filter, reduce

map

1
2
3
4
5
const langs = ['JS', 'HTML', 'CSS'];

const newLangs = langs.map(lang => lang + ' 언어');
console.log(newLangs);
// [ 'JS 언어', 'HTML 언어', 'CSS 언어' ]

filter

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
const langs = ['JS', 'HTML', 'CSS', 0, 1, 2, 3];

const numbers = langs.filter(lang => {
if (typeof lang === 'number') {
return true;
}
});
console.log(numbers);
// [ 0, 1, 2, 3 ]

const strings = langs.filter(lang => {
if (typeof lang === 'string') {
return lang;
}
})
console.log(strings)
// [ 'JS', 'HTML', 'CSS' ]
const isNumber = function (el) {
if (typeof el === 'number') {
return true;
}
}
const numbers = langs.filter(isNumber);
console.log(numbers)

const isString = (el) => typeof el === 'string';
const strings = langs.filter(isString)
console.log(string)

// [ 0, 1, 2, 3 ]
// [ 'JS', 'HTML', 'CSS' ]

reduce

  • 그전에 전형적인 명령어 프로그래밍 방식
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// argument로 받거나
// 인자에 spread형식으로 받거나...

function sumTotal() {
let temp = 0;
for (let i = 0; i < arguments.length; i++) {
temp = temp + arguments[i];
}
return temp;
}

console.log(sumTotal(1, 2, 3, 4, 5, 6, 7));
// 28
function sumTotal(...numbers) {
let temp = 0;
// reduce((누적값, 현재값){},초기값)
return numbers.reduce((total, current) => total + current, 0);
// 첫번째는 초기값(total(0))+ 인자(1)이 total로 들어가고
// 그 다음 total(1)+인자(cur(2))
}

console.log(sumTotal(1, 2, 3, 4, 5, 6, 7));

요소 정렬

sort

1
2
3
4
5
6
7
8
9
10
const numbers = [4, 2, 5, 1, 3];

const orderNumbers = numbers.sort((a, b) => a - b)
console.log(orderNumbers);
// [ 1, 2, 3, 4, 5 ]

const strings = ['마', '가', '라', '나', '다'];
const orderStrings = strings.sort((a, b) => a.localeCompare(b));
console.log(orderStrings);
//[ '가', '나', '다', '라', '마' ]

값 검색

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const strings = ['마', '가', '라', '나', '다'];

const result = strings.find((string) => string === '나');
console.log(result);
// 나 ///없으면 undefinded

const result = strings.findIndex((string) => string === '나');
console.log(result);
// 3

const result = strings.indexOf('나'); // 왼쪽부터,,, lastIndexOf()
console.log(result);
// 3

const result = strings.includes('나');
console.log(result);
// true
[JS] Object 객체

[JS] Object 객체

이채현

객체 생성

일반적으로 생성 할 수 있는 객체

싱글 리터럴 (Literal) 객체

1
2
3
4
const object = {
property: 'value',
method: function () {},
}

생성자 함수 객체 // PascalCase

1
2
3
4
5
function NewObject(name) {
this.name = name;
}

const newObject = new NewObject(’Lee’);

파스칼 케이스 → 암묵적, 명시적 생성자 함수

Object.Create()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const newObject2 = Object.create(Object.prototype, {
name: {
value: 'lee',
writable: true, // 덮어쓸 수 있는지
enumerable: true, // forin문과 같은 것으로 열거할 수 있는지
configurable: true, // 객체 기술자를 수정할 수 있는지
},
});
// const newObject2 = Object.create(프로토타입, 객체 서술자(기술자));

newObject2.name = 'Kim';

for (const key in newObject2) {
console.log(key);
}

프로퍼티 열거

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const obj = {
prop1: 'value1',
prop2: 'value2',
prop3: 'value3',
prop4: 'value4',
};

const prop = 'prop';
const prop1 = 'prop1';

obj.prop1 // value1
obj['prop1'] // value1
obj[prop + 1] // value1
obj[prop1] // value1

for (const key in obj) {
console.log(key); // prop1, prop2, prop3, prop4
console.log(obj[key]); // value1, value2, value3, value4
}

체인닝 방지를 위해

블록 스코프 내에 변수가 없으면 그 위로 찾아 간다…

1
2
3
4
5
6
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
console.log(obj[key]); // value1, value2, value3, value4
}
}
// 상속되거나 확장되어 사용 된 객체의 상위에서 꺼내오지 않도록 하는 방어문

프로퍼티 조작

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const person = {
firstName: "Chaehyeon",
location: "Korea",
}

// 추가
person.lastName: "Lee"
// 수정
person.lastName: "lee"
// 삭제
delet person.location;

person; // { firstName: "Chaehyeon", lastName: "lee" }

// const는 재할당을 막는 것... 그래서 가능

프로퍼티 접근자 (getter, setter)

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
// 추가, 수정, 삭제 등을 접근해서 조작할 때 안전하게 하기 위해
const person = {
_firstName: "Chaehyeon",
lastName: "Lee",

get firstName() {
return this._firstName.toUpperCase();
}

set firstName(newFirstName) {
if (typeof newFirstName !== 'string') {
this._firstName = 'undefinedName';

return;
}

this._firstName = newFirstName;
}

get fullName() {
return this._firstName + this.lastName + '입니다'.
}
}

// console.log(person.firstName); // CHAEHYEON
// console.log(person.fullName); // LeeChaehyeon입니다

person.firstName = 12345;
console.log(person.firstName); // UNDEFINDEDNAME

인스턴스

OOP

인스턴스 === 유일한, 고유한 객체?

객체 하나하나가 곧 인스턴스이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const me = {
name: 'chaehyeon',
age: 26,
locaiton: 'korea',
};

const me2 = {
name: 'chaehyeon',
age: 26,
locaiton: 'korea',
};

console.log(me === me2); // false
console.log(me.name === me2.name); // true

// 객체 그 자체를 비교할 때에는 객체의 메모리 주소를 본다.
// 당연히 메모리주소는 다르다

생성자

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const me = {
name: 'chaehyeon',
age: 26,
locaiton: 'korea',
};

// 객체를 자주 만들고 싶을 때 -> 생성자 함수
function Person(name, age, location) {
this.name = name;
this.age = age;
this.location = location;

this.getName = function () {
return this.name + ' 입니다';
}
}
// 여기서 this는 "생성 될" 인스턴스를 가리킨다.

const me2 = new Person('ChaehyeonLee', 26, 'korea');
// Person { name: 'ChaehyeonLee', age: 26, location: 'korea' }

const you new Person('Kim', 20, 'korea');
// Person { name: 'Kim', age: 20, location: 'korea' }
[JS] DOM 문서 객체 모델

[JS] DOM 문서 객체 모델

이채현

DOM (문서 객체 모델…Document Object Model)

  • HTML ⇒ 문서
  • 문서를 조작하는 언어 ⇒ JavaScript
  • Document Object Model ← JavaScript가 DOM을 통해 HTML을 조작
  • DOM 내부에는 Node: HTML 요소 하나하나 전부를 지칭
    • Node는 Tree형태로 부모노드가 자식노드를, 자식노드가 그 자식노드를…
    • 각 Node에는 수 많은 Properties, Methods…가 존재

결론

  • HTML문서를 JavaScript로 모델링 한 것이 DOM
  • 인터페이스를 하나하나 자르면 단위가 Node이며, Node는 Tree구조로 이루어져있다.

Eg) ’li’ 태그를 뽑아보면 정보가아래처럼 수 많이 담겨있다.

1
2
3
4
5
6
7
8
9
accessKey: ""
ariaAtomic: null
ariaAutoComplete: null
ariaBusy: null
ariaChecked: null
ariaColCount: null
ariaColIndex: null
ariaColSpan: null
...

Dom 선택

자바스크립트에서 HTML 특정 Element들을 가져오는 법 (고전적)

  • Document.getElementById() 문서에 특정 id만 가져오고 싶을 때

  • Element.getElementsByClassName()

    클래스 이름으로… 모든 자식의 엘리먼트를 가져온다. → HTML Collection 형태로 가져온다.

  • Document.getElementsByTagName()

    태그 이름으로 가져온다.

자바스크립트에서 HTML 특정 Element들을 가져오는 법 (비교적 최신)

  • Document.querySelector()

    id(.), class(#), tag name 모두에 사용가능, 첫번째로 보이는 것을 가져온다.

  • document.querySelectorAll()

    해당하는 것을 모두 가져온다. (문자열 O) ⇒ NodeList로 가져오기 때문에 Array로 변환해서 사용한다.

  • 그 외 다양한 조합으로 사용가능하다.

    eg) div.note → div태그 내에 note id를 가져온다.

Dom 조작

조작하기 위한 method들도 매우 많기 때문에 MDN문서를 보면서…


  • innerHTML을 통해서 문자열로 컨트롤 가능하다.
[JS] Algorithm - 자주 쓰이는 구문 정리(22.02.06)

[JS] Algorithm - 자주 쓰이는 구문 정리(22.02.06)

이채현

배열 중복값 개수 구하기

reduce()

reduce() 함수는, 배열의 값을 순회하면서 배열의 값을 특정 형태로 누적하는데 사용합니다.

1
2
3
4
5
6
7
8
9
10
const inputNum = [1, 1, 2, 3, 4, 2, 1];

const result1 = inputNum.reduce((obj, t) => (obj[t] = obj[t] ? obj[t] + 1 : 1, obj), {});

const result2 = inputNum.reduce((obj, t) => {
obj[t] = (obj[t] || 0) + 1;
return obj;
}, {});

// { '1': 3, '2': 2, '3': 1, '4': 1 }

forEach()

1
2
3
const cnt = {}
inputNum.forEach((x) => (cnt[x] ? (cnt[x] += 1) : (cnt[x] = 1)));
// { '1': 3, '2': 2, '3': 1, '4': 1 }

forEach의 callback함수를 풀어쓰면 아래와 같다.

1
2
3
4
5
if(cnt[x]) {
cnt[x] = cnt[x] + 1;
} else {
cnt[x] = 1;
}

즉, 처음에 배열의 첫 번째 값인 ‘1’이 들어오면, cnt[x] (cnt.1)은 undefined이다.

cnt[x]가 undefinded이므로 cnt에 key ‘1’를 추가하고 value 1을 세팅해준다.

이후 다시 ‘1’이 들어오면 cnt[1]은 존재하므로, cnt[1]값 1에 1을 더해준다.


배열에 1 ~ N 값 세팅하기

기본 반복문을 이용

1
2
3
4
5
6
7
8
9
10
11
const arr = [];
const N = 10;

for (let i = 1; i <= N; i++) {
arr.push(i);
}

console.log(arr);
// [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]


ES6의 Array from() and keys() 이용

1
2
3
const arr = Array.from(Array(N).keys());
console.log(arr);
// [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

Spread를 이용한 방법

1
2
3
const arr = [...Array(10).keys()];
console.log(arr);
// [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

1부터 시작하기 위해 from과 length property 이용

1
2
3
const arr = Array.from({length: 10}, (_, i) => i + 1)
console.log(arr);
// [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

단순 원하는 길이만큼 0으로 채우기

1
2
const array = new Array(10001).fill(0);
console.log(array)