<Server Function이라는 First Penguin에 대하여>

강민수·2025년 1월 23일
2

아하 모먼트

목록 보기
9/9

1. First Penguin?

혹시 이 글을 읽고 계신 여러분은 First Penguin이라는 의미를 아는가?

언뜻보면 이 말은 좋다.

하지만, 필자는 이 말의 속뜻이 때로는 마냥 추종하지만은 말라는 뜻처럼 느껴진다.

이유는 간단하다.

만약 그 선구자가 잘못된 길로 인도했다면? 끔찍하다!

물론, 결과론적으로만 봤을 땐, 이게 멀고도 가까운 미래에는 그때가 옳았다 할 수는 있다.

하지만, 불확실성에 모든 것을 걸 수 없기에...

이게 새해 벽두부터 무슨 뜬구름(?) 잡는 얘기? 할 수 있다.

이번 글은 React 18부터 도입되어 React19에는 stable하다고 나온 과거의 Server Action(next는 아직 이렇게 부름) 현재의 Server Function에 대한 얘기다.

다만, 아래의 주의점은 한 번 살펴보고 가자.

주의
이 글은 Server Function에 대한 자세한 설명이나 사용법 등과는 거리가 멀다.

먼저 이렇게 주의를 요하는 이유는 지금부터 필자가 적어내려 갈 부분은 명확하게 정답이 있다기보다는 "이걸 이렇게 보는 사람도 있구나~" 정도로 생각하고 넘어갔으면 싶어서다.

이제부터 우리는 Server Function을 사용하면서 우리가 미쳐 놓치기 쉬운 지점들을 하나씩 곱씹어 볼 예정이다.

2. Server Function이란

허나...

그렇다고 아예 Server Function에 대해 설명을 안하고 넘어갈 수는 없으니...

간략하게 나마 정리하고 짚고는 넘어가 보자.

리액트 공식 문서는 한 줄로 저렇게 표현한다.

즉, Server Function은 클라이언트 컴포넌트가 서버에서 실행되는 비동기 함수를 실행하는 것이다.

사용법은 무척이나 쉬워서, 생략하겠다.

다만, 여기서 생각해 볼 지점과 포인트 하나를 제시하겠다.

자!

다음 코드를 살펴보자.

// createNoteAction.ts
'use server';
  async function createNoteAction() {
    // Server Function
    await db.notes.create();
  }


// EmptyNote.tsx
"use client";
import {createNote} from './actions';

function EmptyNote() {
  console.log(createNote);
  // {$$typeof: Symbol.for("react.server.reference"), $$id: 'createNote'}
  <button onClick={() => createNote()} />
}

간단한 코드다.

EmptyNote라는 클라이언트 컴포넌트가 있고, 이 컴포넌트가 새로운 노트를 만드는 db action을 요청하는 Server Function이다.

무슨 생각이 드시는가?

"그래서 뭐 어쩌란 거지?"

그럴 수 있다.

맞다. 단순해서 더 뭐가 있나 싶다.
필자도 처음에는

"에이 댕 편한데? 미쳤다! 그냥 유저의 인터렉션에 따라 서버를 호출할 수 있다고?"

맞다. 그게 가능한 점은 진짜 큰 장점이다.
다만... 우리가 간과한 점이 있는데...

이제부터 그걸 하나씩 알아보자.

3."use server"라는 그 이름 속의 함정.

자 우리가 살펴본, 코드에서 use server라는 지시자를 자세히 살펴볼 필요가 있다.

이 지시자를 공식문서에서 다시 살펴보자.

공식문서 상에서는 아까 코드에서 봤던 것처럼, 저 지시자를 이용해서 참조하는 거 같다.

일전의 코드에서 주석 처리해 놓은 저 참조 아이디로서, "어 얘 서버 바라보라는 의미네?" 이렇게 인식하는 것으로 추측할 수 있다.(이건 소스 코드를 조금 더 살펴보는 것을 추천드린다.)

// {$typeof: Symbol.for("react.server.reference"), $id: 'createNote'}

1) security considerations

엥? 무슨 보안 고려?

이게 무슨 의미일까?
나도 처음에는 이렇게 생각했다.

이에 대해서는 다시 말씀 드리겠다.

먼저, 공식 문서의 한줄을 살펴보자.

Always treat arguments to Server Functions as untrusted input and authorize any mutations. See security considerations.

Security considerations
Arguments to Server Functions are fully client-controlled. For security, always treat them as untrusted input, and make sure to validate and escape arguments as appropriate.

In any Server Function, make sure to validate that the logged-in user is allowed to perform that action.

-> 요약해 보자면, 서버에서 실행되는 함수이지만, 어디로 튈지 모르는 유저의 행동으로 인해 항상 조심히 다뤄야 한다는 것이다.

이와 관련해서 역시나 유튜브에

아래와 같은 영상도 존재한다.

이 사람도 만약 함부로 쓰면, 그리고 유저한테 노출되는 액션이라서 치명적으로 될 수 있으니 주의하라고 한다.
왜냐면,결국 Server Function이란 것도 클라단(즉, 브라우저 등의 다양한 호출처)의 호출을 통한다.

이 말은 언제든 요청을 가로채서 접근하고 사용할 수 있으며, 제대로 대응해 놓지 않으면 다른 곳에서도 임의로 호출해서 예상치 못 한 결과를 만들어낼 수 있는 위험을 도사리고 있기 때문이다.

그래서 결론을 내 보자면, 서버 가용성 측면에서 하나의 옵션으로서
server action은 좋다.

다만, 잘 알고 주의를 기울여서 써야 된다는 것이다.

요약해 보자면,
1. 최대한 사이드 이펙트가 없는 부분에서 쓸 것.(단순한 요청)
2. 요청 시에, 제대로 된 요청인가(cors, 별도의 보안 토큰 등을 이용해서 검증) 검증할 것.
3. 만약 유저의 정보와 같은 민감한 정보 등이 있다면 최대한 지양할 것.(만약 쓴다고 하더라도 접근 제한이나 예외 처리 등의 주의를 기울일 것)

cf. 참고로 최근에 진행중인 이런 실험도 있다고는 하니 참고 바란다.

Under Construction
To prevent sending sensitive data from a Server Function, there are experimental taint APIs to prevent unique values and objects from being passed to client code.

See experimental_taintUniqueValue and experimental_taintObjectReference.
(민감한 데이터 정보를 서버 function에 넘길 때 암호화 시키는 등의 api도 현재 개발중인 걸로 보인다. 궁금하다면, 위의 링크를 눌러보길 바란다.)

이 내용도 최근 생각해 본 지점 중 하나다.

Server Functions are designed for mutations that update server-side state; they are not recommended for data fetching. Accordingly, frameworks implementing Server Functions typically process one action at a time and do not have a way to cache the return value.

즉, Server Function을 단순히 데이터 페이칭으로 활용하지 말라는 것이다.

왜냐면, 이 값이 결국에는 서버 스테이트로 연동이나 캐싱화가 되게 처리를 해줘야 하는데 그런 용도로서 Server Function은 이용되기 어렵기 때문이라고 한다.

그래서 아직 시기 상조라는 이런 유튜브 영상도 있긴 하다.

그런데 이건 사실 약간 쟁점을 달리 보는 견해도 있다.

아래는 레딧에 올라온 server action을 통해서 데이터 패칭에 대한 티키타카 글인데, 여기서는 사용해도 된다고 보는 견해다.

https://www.reddit.com/r/nextjs/comments/1db36l8/using_server_actions_to_fetch_data_client_side/?rdt=56615

3) 활용 경험 & 개인적인 견해

그래서 필자도 아직 여기에 대해서 뚜렷하게 답을 내리기는 힘들다.

사실 서버 상태 관리를 Server Function에 전적으로 위임하는 것은 아직 시기 상조라는 것에는 동의한다.

다만,

아예 활용하지 못 할것이냐에 대한 답은

아니라고 할 수 있을 거 같다.

최근에 대표적인 활용 경험은 다음과 같다.

클라이언트의 인터렉션 등(무한스크롤, 페이지 네이션 등)의 영향으로 최신화 되어야 할 데이터는 Server Function에서 받아온다.

이후 데이터를 Tanstack Query의 queryClient의 캐싱된 데이터에 주입하는 것으로 활용한 적이 있다.

코드로 간단히 나타내 보면 이러하다.

//server component 
'use server'
async function getData(){
	const {data} = await getData();
  	return data;
}


// client component

'use client'
import {getData} from './sever-action.ts';
import {queryClient} from '@tanstack-query';

// 무한 스크롤 교차시에 함수 실행으로 본다. 
const onIntersect= async()=> {
	const data = await getData();
	queryClient.setQueryData(data);
};

뭐 대충 이런 느낌이다.

물론, 이렇게 사용하는 게 좋을 수도 아닐 수도 있다.

다만, 활용은 할 수 있다 정도로 이해해 주시면 좋을 거 같다.

4. 결론

사실

이 글은

필자의 Server Function에 대한 단상과 더불어 최근의 활용 경험 중심이라 약간 두서가 없었을 수 있다.

다만, 이런 의미를 전달하고 싶었다.

뭔가 특별하고 보물 같아보이는 저 보물 상자 하나처럼.

어떤 기술이라도 충분히 고려해보고 자신의 상황과 조건에 맞게 쓰는 게 중요하다는 것이다.

그런 의미에서

Server Function은 저 보물 상자처럼 묘하고 신기해 보이는 아직은 그런 오묘한 물건이 아닐까?

싶다. ㅎㅎ

이번 글은 여기서 마무리하고, 다음에 다시 새로운 인사이트와 함께 맛있게 돌아오겠다.

끝까지 읽어주신 여러분 모두 새해복 많이 받으시길 바라며~

혹 잘못된 내용이나 의문이 되는 지점은 언제든 댓글로 의견 남겨주시면 감사하겠습니다.

profile
개발도 예능처럼 재미지게~

0개의 댓글

관련 채용 정보