brunch

You can make anything
by writing

C.S.Lewis

by 김민태 Dec 28. 2018

FramerX 스터디 연재#1.1

FramerX 와 함께하는 Code 작성을 위해 알야야할 기초 개념

회사에서 여러가지 이유로 디자이너들과 Framer 스터디를 꾸준히 해왔다. CoffeeScript로 작성하던 Framer 클래식으로 시즌 1 스터디를 마무리하고 시즌 2 스터디는 FramerX 가 공개되는 타이밍에 맞춰 스터디를 다시 시작하게 되었다. 벌써 4번의 스터디를 진행했는데 Framer 클래식때와는 다르게 진도를 빠르게 나가기가 쉽지가 않다. FramerX 도구나 제공되는 문서 등 미성숙한 부분도 진도를 빠르게 진행하는데 걸림돌이 되지만 무엇보다 React 와 TypeScript로 작성되는 코드 형태의 변화가 가능 큰 영향을 끼치는것 같다. 느낌상 10배 이상 진입장벽이 높아진 기분이 든다.


학습해야할 내용이 많다보니 스터디를 진행하면서도 말로만 설명하는데 한계를 느끼고 글로 정리되면 좋겠다는 생각을 했다. 원래는 개인적으로 만든 도구가 있으나 회사 밖 다른 분들도 함께 공유되면 좋겠다 싶어 브런치를 연재 도구로 사용하기로 했다.


코드 이것만은 알고 시작하기

속성 패널에서 Code 속성

요소를 선택한 후 요소의 속성 패널 중 Code 부분을 보면 두 가지 속성이 표시된다.


File - 코드로 제공되는 기능을 선택하기 위한 코드 파일

Override - 선택된 코드 파일에서 제공(export) 하는 기능 목록


FramerX는 새로운 프로젝트를 시작하면 프로젝트 단위로 기본 구성이 만들어진다. Framer 팀은 많은 기능을 포함하고 있는 코드 셋을 제공하기 보다 셈플 코드 몇 개를 제공하기로 맘 먹은것 같다. 모든 프로젝트에는 Examples.tsx 라는 파일이 함께 생성되며 이 파일에서 제공하는 몇 가지 기능을 선택하여 이용할 수 있다.


Scale, Rotate, Fade 등이 그것인데, 이 기능이 유용해서 제공된다기 보다는 이 코드들을 예제 삼아 필요한 기능을 만들어 내라는 의미인 듯 하다. 이 파일의 내용을 살펴보고 각각 의미하는 바가 뭔지 알아보는 것이 첫 번째 연재인 이번 글의 목표이다.


Examples.tsx


.tsx


먼저, 파일의 확장자인 tsx 에 대해 간단히 알아보자. ts 는 TypeScript의 약자다. 즉, FramerX는 Javascript를 사용하지 않고 TypeScript라는 언어를 사용한다. 클래식 버전에서도 Javascript가 아니라 CoffeeScript라는 언어를 사용했었기 때문에 이런 선택이 낯설지는 않다. CoffeeScript와 다른 점이라면 TypeScript는 Javascript를 거의 완전히 지원한다. 그리고 Javascript가 제공하지 못하는 더 많은 기능을 제공하는 언어라고 생각하면 되겠다. 개발자들에겐 많은 기능이 제공되는 것이 득이 될 수 있으나 FramerX를 배우고자 하는 대상은 대부분 디자이너이기 때문에 더 많은 기능은 더 많은 혼란을 가중시킬 수도 있다. 이 점을 염두해 두고 주화입마에 빠지지 않아야 하겠다. ^^


마지막으로 x 가 뜻하는 바는 React 가 제공하는 특별한 문법을 사용한다는 뜻이다. 지금 시점에 React는 뭔지, 그리고 특별한 문법은 또 무엇인지 관심 가지지 말고 그렇군!! 하고 넘어가자. 이후에 Code 기반의 컴포넌트를 만들때 다루어보도록 하겠다.


Edit Examples.tsx 메뉴를 선택하면 에디터가 실행되며 - Visual Studio Code 추천 - 파일을 편집할 수 있다. examples.tsx 파일은 다음과 같은 내용을 포함하고 있으며 새로운 프로젝트를 만들어도 내용은 언제나 동일하다. 하나씩 의미하는 바를 알아보자



잠깐, 코드를 읽기 전에!


프로그래밍 언어는 단순하지만 복잡하다. 응(?) 핵심적인 몇 가지 개념에 대해 짚고 넘어가자. 그렇지 않으면 한 발 한 발 나아가는게 너무나 고통스럽기 때문이다.


우리가 다루게되는 코드는(TypeScript) 아주, 아주~ 크게 보면 4가지 요소로 나누어 볼 수 있다.


1. 값


값은 데이타라고 할 수 있다. 데이타는 1, 2, 10, 102344 같은 숫자부터 "Hello? OK" 같은 문자도 있으며 참(true), 거짓(false) 같이 특수한 값도 있다. 그 외에도 많은 종류가 있는데 모두 값(데이타)로 취급한다.


값의 특징 중에 "불변" 이라는 것이 있다. 무슨 말이냐 하면 값은 변할 수 없다는 것이다. 이 개념이 조금 묘한데, 10 이라는 숫자는 영원히 10 일 뿐 11로 변할 수 없다는 뜻이다. 11은 또 다른 값 11일 뿐 10이 11로 변한게 아니라는 것이다.


값을 데이타라 했는데 데이타를 바꿀 수 없다니? 값을 변경해야하나? 하는 질문이 생길 수 있는데 만약 그래야만 한다면 불변이라는 특징 때문에 다른 개념이 필요하게 되었다.


값이 변해야 하는 상황을 상상해 보자. 100x100 크기의 파란 박스가 있다. 이 박스를 200x200 크기로 점 점 커지며 오른쪽으로 이동하는 애니메이션을 만든다 생각해 보자. 박스 크기의 "값"들이 100에서 200으로 점차 바뀌어야 하고 위치값도 점차 바뀌어야 한다. 즉, 값이 달라져야 하는 상황은 일상적으로 일어나는 일인 것이다.


2. 변수


값은 변할 수 없지만 변해야 하므로 고안된 방식이 변수이다. 변수는 값을 담을 수 있는 그릇이라 할 수 있다. - 많은 오류가 있는 설명이지만 일단 그렇다고 생각하자. - 값 자체는 변경할 수 없지만 값을 담은 그릇 안의 내용물은 다른 값으로 바꿀 수 있다. 변수를 만드는 방법은 다음과 같이 간단하다.


let boxSize = 100
const boxColor = "blue"


let 이란 명령어로 변수 boxSize 를 만들고 100 이란 값을 담겠다 라고 해석할 수 있다.

const 란 명령어로 상수 boxColor 를 만들고 "blue"라는 문자열 값을 담겠다 라고 해석할 수 있다.


변수와 상수는 값을 담는 기능은 동일하다. 다른 점은 변수는 값을 담은 다음 다른 값을 담을 수 있다. 상수는 만들어 질 때 담은 값 이외의 다른 값을 담을 수 없다. 변해야 하는 값은 변수(let)를 변하면 안되는 값은 상수(const)로 만든다. 내가 값을 안바꿀거다! 라고 결심하면 모두 다 변수로 만들어도 상관없다. 우리는 제품을 개발하기 위한 목적으로 코드를 배우는게 아니기 때문에 충분히 느슨하게 배워도 아무 문제가 일어나지 않는다.


3. 식


"계산"하는 모든 행위를 식이라 부른다. 10 + 30 이런것 말이다. 식에 대해선 다음 기회에 조금 더 자세히 배우도록 하자.


4. 문


어떤 값을 다른 값과 비교하여 비교의 결과가 맞다면 A 라는 코드를 실행하고 틀렸다면 B 라는 코드를 실행하게 한다던가, 어떤 코드 A를 10번 반복한다던가 하는 것을 가능하게 하는 문법을 "문" 이라 한다. 문의 종류는 비교문, 반복문 등 등 여러가지가 있는데 문을 사용하게 될 때 해당 문에 대해서 자세히 다뤄보도록 하겠다.


프로그래밍 언어가 제공하는 많은 요소를 아주 큰 범주로 나누어 가볍게 살펴 봤다. 다시 Examples.tsx 의 내용으로 돌아가 보자. 값과 변수에 대해 배웠으니 비슷한 내용이 눈에 들어올 것 같다.


const data = Data({ ... })

export const Scale: Override = () => {
   return {
      ...
   }
}

이 코드들이 대략 비슷한 것 같다. 뭔지 모르는게 여전히 있지만 상수(const) data 와 Scale를 만드는 것 만은 확실한 것 같다. 나머지 아직 배우지 않아 모르겠는 부분에 대해서 알아보자.


객체


값에 대해 설명할 때 값의 종류가 많이 있다고 했다. 객체라는 것도 값의 한 종류다. 다음과 같이 생겼다.

{ boxSize: 100, boxColor: "blue" }

열린 중괄호로 시작해서 닫힌 중괄호까지가 객체라는 하나의 값이다. 이것을 우리가 배운 변수로 표현하면 다음과 같이 다시 써 볼 수 있다.

let boxSize = 100
let boxColor = "blue"

지적 능력이 뛰어난 인간은 본능적으로 많은 자료가 있다면 이것을 범주화 하고 싶어한다. 상자라는 도형이 가지고 있는 특성으로서 크기(Size)와 색(Color)이 있으니 이를 상자라는 공통 요소로 묶을 수 없을까? 같이 말이다. 그래서 같은 범주의 여러개 값을 묶을 수 있는 문법적 양식으로 제공되는 것이 객체라고 이해할 수 있다. 객체도 값이라 했으니 변수에 넣을 수 있다. 이 논리를 이용하여 다음과 같이 써 볼 수 있다.

let box = {
    size: 100,
    color:# "blue"
}
들여쓰기는 읽기 편하도록 한 것이다. 코드에선 공백, 엔터, 들여쓰기 등은 무시되기 때문에 읽기 좋은 모양으로 코드를 꾸미기 위해 공백, 엔터 등을 활용한다.

으로서 하나의 값을 담은 변수가 객체라는 형태의 도움을 받아 여러개의 값을 가질 수 있게 되었다. 정리하자면 객체는 여러개의 값을 담은 변수를 하나로 취급할 때 사용할 수 있는 값이다.


함수

const data = Data({ ... })

위에서 봤던 코드 중 일부인데 Data( ) 괄호 안에 있는 값이 객체다. 그렇다면 Data() 는 무엇일까? 이것 또한 많은 값 중에 배우지 않은 값이다. 값의 종류는 대체 얼마나 많은걸까.. 저것은 함수라는 값이다.


함수란 무엇인가? 객체가 여러개의 값을 담은 변수의 묶음 이라면 함수는 코드를 담은 묶음이라 할 수 있다. 코드를 담았다는건 무엇일까? 아주 간단한 함수를 통해 알아보자.

const double = function(v) {
    let result = v * 2

    return result
}

상수 double 을 만들었다. 담은 값은 코드 묶음인 함수라는 값이다. 코드 묶음인 함수는 몇 가지 특징을 가진다. 첫째 함수는 불리울수 있다. 둘째 함수는 값을 받을 수 있다. 셋째 함수는 값을 돌려줄 수 있다.  하나씩 살펴보자.


함수는 불리울 수 있다
함수는 코드 묶음이라고 했다. 어떤 코드든 묶을 수 있다. 그 코드를 실행시려면 어떻게 해야할까? 함수를 부르면 된다. 부르는 문법은 간단하다. 함수를 다음 그릇에 괄호를 열고 닫아주면 함수가 불려진다.

double() 이렇게 !!!

함수는 값을 받을 수 있다
함수는 불려질 때 값을 전달 받을 수 있다. 위 예제에서 어떻게 그렇게 되는진 모르지만 double 이라는 함수는 값을 주면 그 값을 두배로 만들어서 돌려주도록 동작한다. double(100) 이라고 부르면 200을 돌려준다. double(1000) 이라고 부르면 2000을 돌려준다. 눈치있는 분들은 그렇게 전달된 값이 함수의 v 에 담긴다는 것을 알아차렸을 것이다. v 는 즉, 변수다. 함수의 입력 값을 받는 변수라 하여 인수라고 부른다.

double(2349) 이렇게!!

함수는 값을 돌려줄 수 있다
이미 위에서 얘기 했다. 함수 double 은 100을 주면 200으로 만들어 돌려준다고. 어떻게 돌려주는 것인가? 함수 안에서 return 이란 명령어로 호출한 부분으로 값을 돌려준다. 함수가 돌려주는 값을 받고 싶다면 어떻게할까? .... 그렇다 변수에 담으면 된다.

let returnValue = double(500) 이렇게!!!

변수 returnValue에는 1000 이라는 값이 들어가게 된다.

이제 다시 다음 코드를 보자

const data = Data({ ... })

정확히 뭘 하는 코드인지는 모르겠지만 함수 Data에 입력으로 객체 값을 넘겨주고 그 결과를 상수 data에 담는 코드로 해석할 수 있다. 그럼 이제 Data 함수에 넘겨준 객체를 살펴보자.

{ toggle: true, scale: Animatable(1), opacity: Animatable(1), rotation: Animatable(0), rotationY: Animatable(0) }

이렇게 되어있는데 보기 힘드니 보기 편하게 꾸며보자

{
    toggle: true,
    scale: Animatable(1),
    opacity: Animatable(1),
    rotation: Animatable(0),
    rotationY: Animatable(0)
}

훨씬 읽기 쉬워졌다 ^^. 우선 이 객체는 5개의 값을 가지고 있다. 각각 toggle, scale, opacity, rotation, rotationY. scale 변수에 넣은 값은 Animatable(1) 이라는 값인데 이게 뭔 값인지 몰라도 우린 해석할 수 있다! Animatable라는 함수에게 1이라는 값을 넘겨주고 Animatable가 돌려준 값이 scale에 담긴다는 것을 말이다. 나머지 opacity 등도 마찮가지다. 이제 우리는 Animatable가 무슨 일을 하는지만 이해할 수 있다면 이 코드를 완전하게 이해할 수 있게된다.


Animatable 함수는 FramerX가 제공하는 기능으로 어떤 값이 애니메이션에 사용될 수 있도록 변경해주는 함수다. 좀 더 정확히는 앞으로 매우 자주 사용하게될 animate 함수와 함께 사용할 수 있는 값의 형태로 변경해준다. animate 는 FramerX가 제공하는 특정 값을 다양한 형태로 애니메이션 해주는 함수다.


이쯤에서 맨 첫 번째 라인의 코드를 살펴보자

import { Data, animate, Override, Animatable } from "framer"

import 명령은 어떤 프로그램이 제공하는(export) 기능 중에 필요한 것을 불러오는 방법이다. 여러가지 형태의 import 문법이 있으나 위 코드는 기본적인 형태를 잘 보여준다. 해석해 보면 다음과 같다.


framer 라는 프로그램이 제공(export)하는 Data, animate, Override, Animatable 를 불러온다(import)


가져다 쓰려면 (import) 가져다 쓸 수 있게 제공(export) 해야 한다. Examples.tsx 코드를 보면 export 가 등장하는 코드를 찾을 수 있다.

export const Scale: Override = () => {
    return {
        ...
    }
}

상수 Scale 을 export 하고 있다. export 문법은 간단하다. export 하고 싶은 변수(or 상수) 앞에 export 명령을 붙여주기만 하면 된다. 그런데 여기서 잠깐! Scale 을 export 한다는 것은 누가 import 해서 Scale을 사용한다는 것일까? 그것은 바로 FramerX 의 에디터다. 이 글의 도입부에 FramerX의 에디터의 속성창에서 Examples.tsx 를 선택했을 때 Scale 등이 나타날 수 있었던 이유가 바로 이것이다. Examples.tsx 가 Scale 를 export 하고 있기 때문이다. 앞으로 FramerX 에디터에서 사용할 수 있는 코드를 만들었다면 export 만 해주면 되는 것이다. 비밀 하나가 풀렸다. ^^


자, 이제 몇가지 안남았다. 다음은 Override 에 대해 알아보자.


변수가 저장할 수 있는 값의 유형 지정하기


지금까지 설명한 요소들은 모두 Javascript 문법과 동일한 것이었다. 물론 확장자가 ts 였지만 말이다! 그럼 TypeScript 문법은 없을까? 있다! 바로 변수에 저장할 값의 유형을 고정시킬 수 있는 기능이다. 다음 두 가지 변수를 보자.

let boxSize = 100
let boxColor: string = "blue"

Javascript와 달리 TypeScript는 변수가 담을 수 있는 값의 유형을 - 앞으로 이것을 타입이라 하겠다 - 고정할 수 있다. 그럼 Javascript는 어떻단 말일까? 그렇다, Javascript는 같은 변수에 숫자 타입 값을 넣었다가 필요에 따라 문자도 넣을 수 있고 객체도 넣을 수 있다. 그게 더 편리한 것 아닐까? 라는 생각을 할 것 같다. 장단점이 분명히 존재하지만 안정적인 프로그램을 작성하기 위해선 타입을 지정하는게 좋다는게 일반적인 인식이다. 하지만 프로토타이핑 도구에서 그정도의 안정성이 필요할까? 에 대해선 의구심이 있을 수 있다. 하지만 어쩌겠는가!! Framer 팀이 어떤 이유로 선택한 언어가 TypeScript 인 것을 ^^


개인적인 추측을 해 보면 우리가 작성할 코드의 안정성을 위해서 TypeScript를 선택한 것은 아닌것 같다. 그럼 무엇을 위해? 사용자가 작성한 불안정할 수 있는 코드로 부터 Framer 에디터가 안정적으로 실행되기 위해 선택한 것 아닐까 싶다.


어쨌든! 타입을 지정하는 문법을 알아 봤다. 그래서 다음 코드를 읽을 수 있게 되었다.

export const Scale: Override = () => {

}

Scale 는 Override 타입 값을 담을 수 있는 상수인 것이다. Override 타입이 무엇인지는 설명을 한다해도 지금 이해하긴 어렵기 때문에 export 한 Override 타입의 값만을 Framer 에디터에서 보여준다 라고만 이해하면 될 것 같다.


자, 이제 드디어 마지막이다!!


Scale 상수에 넣고 있는 이상한 유형의 값이 뭔지 알아보자.

export const Scale: Override = () => {
    return {
        ...
    }
}

위 코드에서 설명하지 않는 형태는 바로 () => { }  이것이다. 이것은 무엇일까? 상수에 넣을 수 있는 것을 보면 값인건 분명하다. 그런데 무엇인지 모르겠다! 코드를 자세히 보면 힌트가 있다. 바로 return { ... } 그렇다 앞서 우리는 함수가 값을 반환할 때 return 명령을 사용한다고 배웠다. () => { } 이것은 바로 함수인 것이다.


함수라면 function() { } 이런 모양이어야 할텐데 왜 모양이 저럴까? 함수를 표현하는 문법이 여러가지가 있다. 위 코드의 함수 표현은 화살표 모양을 닮았다 하여 Arrow Function 즉, 화살표 함수라고 부른다. 말하자면 함수의 축약형인 것이다. 다음 두개의 함수는 우리 관점에선 완전히 동일하며 표현만 다르다.

const doubleA = function(v) {
    let result = v * 2

    return result
}

const doubleB = (v) => {
    let result = v * 2

    return result
}

왜 굳이 축약형을 쓸까? 여러가지 이유가 있지만 지금은 여러가지 표현 방법을 제공하는 것이고, 편한 방법을 선택하여 사용할 수 있다고만 알아두자. ^^


이제 코드를 읽기 위한 기본적인 준비가 되었다. 다음 연재에선 실제로 간단한 요소로 인터렉션을 만들어 보며 좀 더 이해의 폭을 넓히고 FramerX의 작동 원리를 알아가보도록 하자.

매거진의 이전글 [번역] 컴퓨터 학습 모델 해석 실습
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari