Transcript
00:00:00처음에는 JSX가 있었고, 그 다음에는 TSX가 나왔지만, 우리는 수년간 여기에 머물러 있었습니다.
00:00:04이걸 더 개선할 수는 없을까요? 어쩌면 TSRX가 답이 될지도 모릅니다.
00:00:08기존 것과 비슷하면서도 다릅니다.
00:00:10함수(function) 대신 컴포넌트(component)를 쓰고, 텍스트에는 문자열을 사용하며,
00:00:14안에는 일반적인 if문이 있고, return문도 따로 없습니다.
00:00:17그렇다면 이것은 무엇이고, 왜 등장했으며, 사용해야 할까요? 함께 알아봅시다.
00:00:21[음악]
00:00:26아마 여러분 중 몇 분은 이런 코드를 실제로 본 적이 있을 텐데요,
00:00:29이것은 사실 Ripple의 제작자가 만든 것이기 때문입니다.
00:00:31Rich가 6개월 전에 이 채널에서 다뤘던 새로운 프론트엔드 프레임워크입니다.
00:00:35그러니 이런 소식을 계속 접하려면 구독해 주세요.
00:00:38이들이 한 일은 Ripple에서 사용되던 구문을 추출하여,
00:00:41React, Preact, Solid, Vue, 그리고 당연히 Ripple에서도 작동하게 만든 것입니다.
00:00:45많은 사람들이 이에 대해 꽤나 열광했습니다.
00:00:47TSRX는 가독성을 유지하면서 코드를 한곳에 모아 UI 컴포넌트를 작성하는 방법이라고 소개합니다.
00:00:52즉, 구조, 스타일링, 제어 흐름이 함께 공존하며,
00:00:55그 결과물은 TypeScript와 완전히 하위 호환성을 유지합니다.
00:00:58하지만 이전에 Ripple을 사용해 본 적이 없다면 여전히 그게 무슨 뜻인지 헷갈리실 테니,
00:01:01기능들을 하나씩 살펴보겠습니다.
00:01:03우선 TSRX 파일을 사용하기 때문에 컴파일 단계가 필요하지만,
00:01:07Vite 플러그인을 사용하면 설정이 정말 간단합니다.
00:01:10또한 다른 프레임워크나 런타임을 위한 다양한 옵션도 준비되어 있습니다.
00:01:13실제 컴포넌트의 경우, 여기에 function 대신 component라고 작성하는데,
00:01:17이것은 주로 컴파일러 자체를 위한 키워드이지만,
00:01:20여기에 렌더링 로직이 포함될 것임을 명확하게 보여주기도 합니다.
00:01:24개발자 경험을 높여주는 소소한 개선이라고 볼 수 있겠네요.
00:01:27한 가지 눈에 띄는 점은 여기에 return문이 없다는 것입니다.
00:01:30그 이유는 TSRX가 선언문 기반(statement-based) JSX를 사용하기 때문인데,
00:01:33따라서 컴포넌트 트리를 반환할 필요 없이,
00:01:35렌더링하고 싶은 위치에 마크업을 그냥 작성하면 됩니다.
00:01:37즉, 컴포넌트 상단의 이 카드 위에 다른 p 태그를 그냥 툭 던져 놓으면,
00:01:42작성된 위치에 그대로 렌더링이 된다는 의미입니다.
00:01:44컴포넌트 내에서 여전히 return을 사용할 수 있지만, 값 없이 단독으로만 써야 하며,
00:01:47조기 반환(early return) 목적으로만 사용되어 그 이후의 UI와 로직은 건너뛰게 됩니다.
00:01:51TSRX 컴포넌트는 매우 선형적이라고 생각하면 이해하기 쉽습니다.
00:01:54소스 코드를 작성한 순서가 곧 렌더링 순서가 되며,
00:01:57단순히 위에서 아래로 읽어 내려가면 됩니다.
00:01:59하지만 이로 인해 컴포넌트가 무엇을 렌더링하는지 한눈에 파악하기가 더 어려워질 수도 있습니다.
00:02:03React 같은 곳에서는 보통 바로 return문으로 건너뛰어 확인하니까요.
00:02:06선언문 기반 JSX의 또 다른 장점은 순수 자바스크립트를 훨씬 많이 사용할 수 있다는 점입니다.
00:02:10예를 들어, 조건부 렌더링이 정말 간단해집니다.
00:02:13필요하다면 else-if와 else가 포함된 그냥 일반 if문입니다.
00:02:17조건 안에는 그저 JSX를 하나의 선언문으로 배치하기만 하면 됩니다.
00:02:20React에서 이와 동일한 형태는 종종 중첩된 삼항 연산자로 변하곤 합니다.
00:02:23JSX에서는 모든 분기가 식(expression)이어야 하기 때문입니다.
00:02:26그래서 저는 TSRX 버전이 때로는 더 읽기 편하다고 느낍니다.
00:02:29특히 더 복잡한 조건문이 있을 때 말이죠.
00:02:31하지만 반대로, 아주 단순한 조건만 필요한 경우에는
00:02:35오히려 코드가 더 장황해질 수도 있겠다는 생각도 듭니다.
00:02:37switch문 역시 마찬가지입니다.
00:02:39일반 자바스크립트 switch문을 사용하여 각 케이스와
00:02:41그에 맞춰 렌더링할 JSX를 작성하면 됩니다.
00:02:44이는 동일한 패턴을 구현하기 위해 별도의 함수가 필요한
00:02:47React에서의 처리 방식보다 조금 더 간단합니다.
00:02:49그래서 이 부분은 TSRX가 조금 더 깔끔해 보입니다.
00:02:51하지만 개인적으로 리스트 렌더링 부분은 TSRX가 덜 마음에 듭니다.
00:02:55여기서는 .map을 버리고 대신 for-of 루프를 사용하는데,
00:02:58TSRX는 이 루프를 확장하여 인덱스뿐만 아니라
00:03:01key와 함께 고유한 식별자(identity)도 얻을 수 있게 만들었습니다.
00:03:03그리고 특정 아이템을 건너뛰고 싶을 때는 단순히 continue를 사용하면 됩니다.
00:03:06이 역시 순수 자바스크립트의 흐름에 더 가깝습니다.
00:03:08하지만 앞서 말했듯이, 저는 .map이나 filter 등을 사용하는 데 너무 익숙해져 있어서,
00:03:12기존 방식을 고수하게 될 것 같습니다.
00:03:14그리고 다른 루프 타입인
00:03:17for, for-in, while, do-while 등은 사용할 수 없다는 점도 유의해야 합니다.
00:03:19이 방식은 오직 for-of 루프에서만 작동합니다.
00:03:21순수 자바스크립트를 사용하는 흐름을 이어가자면,
00:03:23TSRX에서 에러 바운더리(error boundary)를 처리하는 방법은 단순한 try-catch 블록입니다.
00:03:27기교가 들어가지 않아 꽤 직관적입니다.
00:03:30그리고 비동기 바운더리(async boundary)가 필요한 경우에도 이 try-catch 블록을 그대로 사용할 수 있는데,
00:03:33단순히 pending 블록을 추가하고
00:03:35그 안에 로딩 컴포넌트를 작성하기만 하면 됩니다.
00:03:38컴파일러는 실제로 이 코드를 가져가서
00:03:40사용 중인 프레임워크에 맞게 변환해 줍니다.
00:03:42React, Preact, Solid의 경우 실제로는 lazy를 사용하고,
00:03:45Ripple에서는 그에 상응하는 Ripple 기능으로 변환됩니다.
00:03:47특히 React에 대해 이야기하자면,
00:03:48지금까지 살펴본 기능들은
00:03:50React의 핵심 규칙 중 하나를 어길 수 있게 해주는 것처럼 보입니다.
00:03:53바로 Hook의 규칙(rule of hooks)입니다.
00:03:54이제 Hook을 조건문이나 조기 반환 뒤에 배치할 수 있으며,
00:03:57심지어 루프 내부에도 둘 수 있습니다.
00:03:58모두 정상적으로 작동할 것입니다.
00:04:00덕분에 코드를 실제로 필요한 위치에 더 잘 모아둘 수 있으며,
00:04:03최종 출력물은 심지어 규칙을 깨뜨리지도 않습니다.
00:04:06컴파일러가 생성된 함수의 최상단으로 모든 Hook을 조용히 끌어올려(hoisting) 주기 때문에,
00:04:09React는 여전히 안정적인 순서로 Hook을 보게 되지만,
00:04:11개발자는 실제로 소속되어야 할 위치에 코드를 작성할 수 있는 것입니다.
00:04:14수년 동안 React를 사용해 온 제 입장에서는,
00:04:16이 기능이 가장 적응하기 힘든 것 중 하나였습니다.
00:04:18또한 이 기능은 컴파일러가
00:04:20보이지 않는 곳에서 많은 마법을 부리게 만듭니다.
00:04:22특히 특정 프레임워크에 맞춰서 말이죠.
00:04:24그래서 만약 이 코드를 디버깅해야 한다면,
00:04:26어떤 코드가 어디에 있는지 헤맬 수도 있을 것 같습니다.
00:04:28하지만 다음으로 소개할 렉시컬 스코핑(lexical scoping)의 경우,
00:04:30모든 중첩된 요소가 자신만의 스코프를 생성합니다.
00:04:32그래서 여기 세 개의 서로 다른 div 블록 안에 const label을 선언할 수 있고,
00:04:36이들은 서로 충돌하지 않습니다.
00:04:37심지어 여기 함수 최상단에 아무도 읽지 않는 label이 하나 더 있어도,
00:04:40마찬가지로 충돌이 발생하지 않습니다.
00:04:41모든 if, for, switch, try문에서도 동일하게 적용됩니다.
00:04:44각각 고유한 스코프를 가지므로,
00:04:46선언한 변수, 실행하는 함수,
00:04:48그리고 도출해 낸 값들이 다른 스코프로 흘러 들어가지 않습니다.
00:04:51이 역시 코드를 한곳에 모으는 데 중점을 둔 기능이며,
00:04:54컴포넌트를 위에서 아래로 읽히는 선형적인 방식으로 만들어 줍니다.
00:04:57자바스크립트 이야기에서 분위기를 바꾸어 스타일링에 대해 이야기해 봅시다.
00:05:00TSRX에서는 실제로 스코프 스타일(scoped style)을 제공합니다.
00:05:02컴포넌트 안에 style 블록을 그냥 넣으면,
00:05:04그 안에 작성한 CSS는 오직 해당 컴포넌트에만 제한(scoped)되며,
00:05:08컴파일 시 클래스 이름에 고유한 해시값이 부여됩니다.
00:05:11이 카드 컴포넌트는 card 클래스를 가지고 있으며,
00:05:13여기서도 card 클래스를 사용하려 하고 있지만,
00:05:16카드 스타일을 전혀 적용받지 못합니다.
00:05:17자체적인 style 블록을 가지고 있지 않기 때문입니다.
00:05:19부모로부터 스타일을 물려받지 않는 이유는
00:05:21그 고유한 해시값을 가지고 있지 않기 때문입니다.
00:05:22만약 컴포넌트 간에 스타일을 공유하고 싶다면,
00:05:24TSRX에 style 키워드가 있습니다.
00:05:26부모가 이 키워드를 사용해 스타일 이름을 전달하면,
00:05:29className을 prop으로 받는 컴포넌트에서
00:05:31생성된 그 고유 해시값이 함께 전달되도록 보장합니다.
00:05:35이제 부모와 동일한 스타일을 가지게 된 것을 확인할 수 있습니다.
00:05:37기술적으로 스타일 주변에 global 셀렉터를 사용하여
00:05:40스코프를 벗어나 스타일을 전역으로 적용할 수도 있지만,
00:05:42그렇게 하면 코드가 조금 지저분해지고
00:05:44스타일이 어디서 오는지 파악하기 어려워질 것 같습니다.
00:05:46개인적으로 저는 완전히 뼈속까지 Tailwind파라서,
00:05:48이 기능을 많이 쓰지는 않고
00:05:50그냥 Tailwind를 계속 사용할 것 같지만,
00:05:51그럼에도 꽤 멋진 기능입니다.
00:05:53다음은 집중해서 보신 분들을 위한 기능입니다.
00:05:56제가 보여드린 코드 블록에서
00:05:57문들이 텍스트를 처리하는 방식에 미세한 차이가 있었습니다.
00:06:01요소 내부의 순수 텍스트는 반드시 큰따옴표로 감싸야 합니다.
00:06:04JSX에서처럼 그냥 텍스트만 적을 수는 없습니다.
00:06:07하지만 여전히 동적 값을 사용할 수 있으며,
00:06:08이 줄처럼 말이죠,
00:06:10이것은 두 개의 큰따옴표 문자열 사이에 있고,
00:06:12TSRX는 컴파일할 때 이를 단순히 하나의 문자열로 결합합니다.
00:06:16또 다른 옵션은 단순히 템플릿 리터럴을 사용하는 것입니다.
00:06:19결과는 동일합니다.
00:06:20저에게는 이 부분이 TSRX를 사용하면서 겪은 소소한 불편함 중 하나였는데,
00:06:23텍스트에 따옴표를 쓰지 않는 습관이 너무 강력하게 몸에 배어 있기 때문입니다.
00:06:26또 다른 텍스트 관련 기능으로는,
00:06:27TSRX가 실제 HTML 마크업이 포함된 문자열을 처리할 수 있다는 점인데,
00:06:31이를 렌더링하는 두 가지 방법이 있습니다.
00:06:33첫 번째는 단순히 text 키워드를 사용하는 것으로,
00:06:35이스케이프된 텍스트를 렌더링하여
00:06:38글자 그대로의 HTML 문자열을 볼 수 있게 해주며,
00:06:40크로스 사이트 스크립팅(XSS)으로부터도 안전합니다.
00:06:42따라서 어떤 값이 확실히 문자열임을 보장하고 싶고,
00:06:45그 문자열의 출처가 다소 모호하여
00:06:48코드를 작성할 때 그 타입을 명확히 알 수 없는 경우에 유용합니다.
00:06:51두 번째 옵션은 문자열을 실제 HTML로 렌더링하고 싶은 경우로,
00:06:54단순히 html 키워드를 사용하면 되고,
00:06:56이것은 문자열을 실제 HTML로 파싱합니다.
00:06:58하지만 이 기능은 오직 Ripple에서만 작동하며, Vue는 이의 아주 제한적인 부분만 지원합니다.
00:07:02React와는 관련이 없지만,
00:07:03Ripple, Vue, 또는 Solid를 사용하시는 분들이
00:07:06흥미로워할 만한 또 다른 기능은 지연 구조분해 할당(lazy destructuring)입니다.
00:07:08이러한 프레임워크에서 평소처럼 props를 구조분해 할당하면,
00:07:10호출 시점에 각 값을 스냅샷으로 캡처하게 되어,
00:07:12접근할 때마다 발생하는 반응성(per-access reactivity)이 깨지게 됩니다.
00:07:14그래서 TSRX에서는 props 앞에 앰퍼샌드(&)를 붙이기만 하면 되는데,
00:07:18구조분해 할당처럼 보이지만,
00:07:20실제 각 바인딩은 컴파일될 때 사용되는 위치의 프로퍼티 조회 코드로 다시 변환됩니다.
00:07:23따라서 런타임은 각각의 접근을 개별적으로 감지하고,
00:07:25시그널(signal) 업데이트가 여전히 리렌더링을 트리거하므로,
00:07:28구조분해 할당이 주는 편리함을 그대로 유지하면서도
00:07:30프레임워크의 반응성도 지켜낼 수 있습니다.
00:07:32마지막으로 보여드릴 기능은 간단하고 유용한 편의 기능입니다.
00:07:35prop에 전달하는 값의 이름이 그 prop과 동일한 이름을 가졌던 적이 있으신가요?
00:07:40가장 흔하게는 on-change 함수 같은 곳에서 말이죠.
00:07:42TSRX를 사용하면 이를 자바스크립트 객체와 유사하게
00:07:45단축 표기법(shorthand)으로 간단히 작성할 수 있습니다.
00:07:47깔끔하고 단순하죠.
00:07:49종합적으로 볼 때, TSRX는 일반적인 자바스크립트의 흐름을 다시 JSX에 녹여내고
00:07:53소소한 편의 기능을 추가하려는 시도로 보이며,
00:07:55꽤 많은 요소가 마음에 듭니다.
00:07:57진짜 아쉬운 점은, AI가 대부분의 코드를 작성하는 현시점에
00:08:01이 기술이 너무 마이너하고 너무 늦게 나왔다는 것입니다.
00:08:03그리고 AI가 잘 작성하는 코드는 주로 JSX와 React이니까요.
00:08:07그렇긴 하지만, Claude에게 TSRX를 사용한 데모를 몇 개 던져주었더니,
00:08:10공식 사이트의 LLM.txt 파일만 보고도 코드를 잘 작성해 내긴 했습니다.
00:08:14그럼에도 저는 그냥 일반 React를 계속 사용할 것 같습니다.
00:08:17또 다른 단점은, 특히 React의 경우,
00:08:19그 위에 더 많은 컴파일러 마법을 얹는 느낌이 들고,
00:08:21제가 수년 동안 익혀온 개발 흐름을 깨뜨리는 것 같기도 합니다.
00:08:24그래서 개인적으로 저에게는 맞지 않지만, 그렇다고 이것이 나쁘다는 뜻은 아닙니다.
00:08:27Svelte에서 넘어오신 분들은 이 방식을 아주 좋아하실 것 같고,
00:08:30이미 Ripple을 사용하고 계신 분들이라면 이것을 이미 사랑하고 계실 겁니다.
00:08:33그러니 마음에 드신다면 아래 댓글로 알려주시고,
00:08:35구독도 해주시고, 늘 그렇듯 다음 영상에서 뵙겠습니다.
00:08:40[음악]