posts

AI Assistant

Oct 1, 2025 updated Oct 1, 2025 architectureexpoproducttypescript

Stackflow Activity vs Step 완벽 가이드

🎯 Activity vs Step 명확한 구분 기준

핵심 판단 기준 4가지

1. 진입점 기준 (가장 중요)

  • Activity: 여러 곳에서 직접 진입 가능
  • Step: 특정 Activity에서만 진입 가능
// ✅ Activity - 다양한 곳에서 진입 가능
flow.push('ActLogin', {});           // 메인에서
flow.push('ActLogin', {});           // 설정에서  
flow.push('ActLogin', {});           // 에러 발생시

// ✅ Step - 특정 Activity에서만 진입
// 로그인 Activity 내부에서만 접근
stepPush({ showPasswordReset: true });

2. URL/라우팅 기준

  • Activity: 독립적인 URL을 가져야 함
  • Step: URL 없이 상태로만 관리
// ✅ Activity
// /login
// /signup  
// /dashboard

// ✅ Step  
// /login (상태: showPasswordReset)
// /signup (상태: currentStep = 2)

3. 브라우저 히스토리 기준

  • Activity: 뒤로가기 버튼으로 이전 Activity로 이동
  • Step: 뒤로가기 시 이전 Step이나 Activity 내부 상태 변경

4. 재사용성 기준

  • Activity: 앱의 다른 부분에서 재사용 가능
  • Step: 특정 Activity에 종속되어 재사용 불가

📋 결정 체크리스트

다음 질문들로 판단하세요:

  1. 이 화면에 직접 링크를 걸 수 있나요?

    • Yes → Activity / No → Step
  2. 앱의 다른 부분에서 이 화면으로 직접 이동할 일이 있나요?

    • Yes → Activity / No → Step
  3. 이 화면이 독립적인 기능을 제공하나요?

    • Yes → Activity / No → Step
  4. 브라우저 새로고침 시에도 이 화면이 유지되어야 하나요?

    • Yes → Activity / No → Step

🏗️ Stackflow 기본 구조 이해

1. Activity 생성 방법

// MyActivity.tsx
import { ActivityComponentType } from "@stackflow/react";
import { AppScreen } from "@stackflow/plugin-basic-ui";

const MyActivity: ActivityComponentType = () => {
  return (
    <AppScreen appBar={{ title: "My Activity" }}>
      <div>My Activity</div>
    </AppScreen>
  );
};

export default MyActivity;

2. Stackflow 설정

// stackflow.ts
import { stackflow } from "@stackflow/react";
import { basicRendererPlugin } from "@stackflow/plugin-renderer-basic";
import { basicUIPlugin } from "@stackflow/plugin-basic-ui";
import MyActivity from "./MyActivity";

export const { Stack, useFlow } = stackflow({
  transitionDuration: 350,
  plugins: [
    basicRendererPlugin(),
    basicUIPlugin({
      theme: "cupertino",
    }),
  ],
  activities: {
    MyActivity,
  },
  initialActivity: () => "MyActivity", // 초기 Activity 설정
});

3. 파라미터가 있는 Activity

// Article.tsx
import { ActivityComponentType } from "@stackflow/react";
import { AppScreen } from "@stackflow/plugin-basic-ui";

type ArticleParams = {
  title: string;
};

const Article: ActivityComponentType<ArticleParams> = ({ params }) => {
  return (
    <AppScreen appBar={{ title: "Article" }}>
      <div>
        <h1>{params.title}</h1>
      </div>
    </AppScreen>
  );
};

export default Article;

🔄 Activity 네비게이션

Activity 이동 (push)

import { ActivityComponentType } from "@stackflow/react";
import { AppScreen } from "@stackflow/plugin-basic-ui";
import { useFlow } from "./stackflow";

const MyActivity: ActivityComponentType = () => {
  const { push } = useFlow();

  const onClick = () => {
    push("Article", {
      title: "Hello",
    });
  };

  return (
    <AppScreen appBar={{ title: "My Activity" }}>
      <div>
        My Activity
        <button onClick={onClick}>Go to article page</button>
      </div>
    </AppScreen>
  );
};

Activity 교체 (replace)

const MyActivity: ActivityComponentType = () => {
  const { replace } = useFlow();

  const onClick = () => {
    replace("Article", {
      title: "Hello",
    });
  };

  return (
    <AppScreen appBar={{ title: "My Activity" }}>
      <div>
        My Activity
        <button onClick={onClick}>Go to article page</button>
      </div>
    </AppScreen>
  );
};

📚 Step 관리

Step 추가하기

import { ActivityComponentType } from "@stackflow/react";
import { AppScreen } from "@stackflow/plugin-basic-ui";
import { useStepFlow } from "./stackflow";

type ArticleParams = {
  title: string;
};

const Article: ActivityComponentType<ArticleParams> = ({ params }) => {
  // 타입 안정성을 위해 현재 액티비티의 이름을 넣어줘요
  const { stepPush } = useStepFlow("Article");

  const onNextClick = () => {
    // stepPush()을 호출하면 params.title이 변경돼요.
    stepPush({
      title: "Next Title",
    });
  };

  return (
    <AppScreen appBar={{ title: "Article" }}>
      <div>
        <h1>{params.title}</h1>
        <button onClick={onNextClick}>next</button>
      </div>
    </AppScreen>
  );
};

Step 교체하기

const Article: ActivityComponentType<ArticleParams> = ({ params }) => {
  const { stepReplace } = useStepFlow("Article");

  const onChangeClick = () => {
    // stepReplace()을 호출하면 params.title이 변경돼요
    stepReplace({
      title: "Next Title",
    });
  };

  return (
    <AppScreen appBar={{ title: "Article" }}>
      <div>
        <h1>{params.title}</h1>
        <button onClick={onChangeClick}>change</button>
      </div>
    </AppScreen>
  );
};

Step 삭제하기

const Article: ActivityComponentType<ArticleParams> = ({ params }) => {
  const { stepPop } = useStepFlow("Article");

  const onPrevClick = () => {
    // stepPop()을 호출하면 이전 params.title로 돌아가요
    stepPop();
  };

  return (
    <AppScreen appBar={{ title: "Article" }}>
      <div>
        <h1>{params.title}</h1>
        <button onClick={onPrevClick}>prev</button>
      </div>
    </AppScreen>
  );
};

🎨 실제 프로젝트 적용 예제

로그인 관련 화면 구조

// 📁 프로젝트 구조
activities/
├── act-login.tsx        // Activity
├── act-signup.tsx       // Activity  
├── act-dashboard.tsx    // Activity
└── components/
    ├── login-form.tsx
    ├── password-reset-form.tsx  // Step 내용
    └── find-id-form.tsx         // Step 내용

Activity 구현 예제

// act-login.tsx
import { ActivityComponentType } from "@stackflow/react";
import { AppScreen } from "@stackflow/plugin-basic-ui";
import { useStepFlow } from "../stackflow";
import LoginForm from "../components/login-form";
import PasswordResetForm from "../components/password-reset-form";
import FindIdForm from "../components/find-id-form";

type LoginParams = {
  step?: 'login' | 'resetPassword' | 'findId';
};

const ActLogin: ActivityComponentType<LoginParams> = ({ params }) => {
  const { stepPush, stepPop } = useStepFlow("ActLogin");
  const currentStep = params.step || 'login';

  const showPasswordReset = () => {
    stepPush({ step: 'resetPassword' });
  };

  const showFindId = () => {
    stepPush({ step: 'findId' });
  };

  const goBack = () => {
    stepPop();
  };

  return (
    <AppScreen appBar={{ title: "로그인" }}>
      {currentStep === 'login' && (
        <LoginForm 
          onShowPasswordReset={showPasswordReset}
          onShowFindId={showFindId}
        />
      )}
      {currentStep === 'resetPassword' && (
        <PasswordResetForm onBack={goBack} />
      )}
      {currentStep === 'findId' && (
        <FindIdForm onBack={goBack} />
      )}
    </AppScreen>
  );
};

export default ActLogin;

Activity vs Step 분류 결과

// ✅ Activity로 분류된 화면들
- ActLogin (로그인)
  - 이유: 메인, 보호된 페이지, 만료시 등 여러 곳에서 진입
  - URL: /login

- ActSignup (회원가입)  
  - 이유: 로그인 페이지, 마케팅 페이지 등에서 진입
  - URL: /signup

- ActDashboard (대시보드)
  - 이유: 로그인 후 메인 화면, 독립적 접근 필요
  - URL: /dashboard

// ✅ Step으로 분류된 화면들
- 비밀번호 찾기
  - 이유: 로그인 페이지에서만 진입, URL 없음
  - 구현: ActLogin의 step 파라미터로 관리

- 아이디 찾기
  - 이유: 로그인 페이지에서만 진입, URL 없음  
  - 구현: ActLogin의 step 파라미터로 관리

- 본인인증
  - 이유: 회원가입 과정의 한 단계
  - 구현: ActSignup의 step 파라미터로 관리

🎯 핵심 정리

Activity를 사용해야 할 때

  • 독립적인 URL이 필요한 경우
  • 여러 곳에서 직접 접근해야 하는 경우
  • 브라우저 새로고침 후에도 해당 화면이 유지되어야 하는 경우
  • 앱의 주요 네비게이션 대상인 경우

Step을 사용해야 할 때

  • 특정 Activity의 일부 과정인 경우
  • 하나의 큰 기능을 여러 단계로 나누는 경우
  • 이전/다음 단계의 맥락이 중요한 경우
  • URL 없이 상태로만 관리해도 되는 경우

이 기준을 따르면 Stackflow에서 Activity와 Step을 명확하게 구분하여 사용할 수 있습니다.

Me

마크다운으로 작성해줘

AI Assistant

# Stackflow Activity vs Step 완벽 가이드

## 🎯 Activity vs Step 명확한 구분 기준

### 핵심 판단 기준 4가지

#### 1. **진입점 기준** (가장 중요)
- **Activity**: 여러 곳에서 직접 진입 가능
- **Step**: 특정 Activity에서만 진입 가능

typescript // ✅ Activity - 다양한 곳에서 진입 가능 flow.push('ActLogin', {}); // 메인에서 flow.push('ActLogin', {}); // 설정에서 flow.push('ActLogin', {}); // 에러 발생시 // ✅ Step - 특정 Activity에서만 진입 // 로그인 Activity 내부에서만 접근 stepPush({ showPasswordReset: true });


#### 2. **URL/라우팅 기준**
- **Activity**: 독립적인 URL을 가져야 함
- **Step**: URL 없이 상태로만 관리

typescript // ✅ Activity // /login // /signup // /dashboard // ✅ Step // /login (상태: showPasswordReset) // /signup (상태: currentStep = 2)


#### 3. **브라우저 히스토리 기준**
- **Activity**: 뒤로가기 버튼으로 이전 Activity로 이동
- **Step**: 뒤로가기 시 이전 Step이나 Activity 내부 상태 변경

#### 4. **재사용성 기준**
- **Activity**: 앱의 다른 부분에서 재사용 가능
- **Step**: 특정 Activity에 종속되어 재사용 불가

## 📋 결정 체크리스트

다음 질문들로 판단하세요:

1. **이 화면에 직접 링크를 걸 수 있나요?** 
   - Yes → Activity / No → Step

2. **앱의 다른 부분에서 이 화면으로 직접 이동할 일이 있나요?**
   - Yes → Activity / No → Step

3. **이 화면이 독립적인 기능을 제공하나요?**
   - Yes → Activity / No → Step

4. **브라우저 새로고침 시에도 이 화면이 유지되어야 하나요?**
   - Yes → Activity / No → Step

## 🏗️ Stackflow 기본 구조 이해

### 1. Activity 생성 방법

typescript // MyActivity.tsx import { ActivityComponentType } from "@stackflow/react"; import { AppScreen } from "@stackflow/plugin-basic-ui"; const MyActivity: ActivityComponentType = () => { return ( <AppScreen appBar={{ title: "My Activity" }}> My Activity ); }; export default MyActivity;


### 2. Stackflow 설정

typescript // stackflow.ts import { stackflow } from "@stackflow/react"; import { basicRendererPlugin } from "@stackflow/plugin-renderer-basic"; import { basicUIPlugin } from "@stackflow/plugin-basic-ui"; import MyActivity from "./MyActivity"; export const { Stack, useFlow } = stackflow({ transitionDuration: 350, plugins: [ basicRendererPlugin(), basicUIPlugin({ theme: , activities: { MyActivity, }, initialActivity: () => "MyActivity", // 초기 Activity 설정 }); latex_unknown_tag


### 3. 파라미터가 있는 Activity

```typescript
// Article.tsx
import { ActivityComponentType } from "@stackflow/react";
import { AppScreen } from "@stackflow/plugin-basic-ui";

type ArticleParams = {
  title: string;
};

const Article: ActivityComponentType<ArticleParams> = ({ params }) => {
  return (
    <AppScreen appBar={{ title: "Article" }}>
      <div>
        <h1>{params.title}</h1>
      </div>
    </AppScreen>
  );
};

export default Article;
## 🔄 Activity 네비게이션
### Activity 이동 (push)
``` typescript
import { ActivityComponentType } from "@stackflow/react";
import { AppScreen } from "@stackflow/plugin-basic-ui";
import { useFlow } from "./stackflow";

const MyActivity: ActivityComponentType = () => {
  const { push } = useFlow();

  const onClick = () => {
    push("Article", {
      title: "Hello",
    });
  };

  return (
    <AppScreen appBar={{ title: "My Activity" }}>
      <div>
        My Activity
        <button onClick={onClick}>Go to article page</button>
      </div>
    </AppScreen>
  );
};

Activity 교체 (replace)

const MyActivity: ActivityComponentType = () => {
  const { replace } = useFlow();

  const onClick = () => {
    replace("Article", {
      title: "Hello",
    });
  };

  return (
    <AppScreen appBar={{ title: "My Activity" }}>
      <div>
        My Activity
        <button onClick={onClick}>Go to article page</button>
      </div>
    </AppScreen>
  );
};

📚 Step 관리

Step 추가하기

import { ActivityComponentType } from "@stackflow/react";
import { AppScreen } from "@stackflow/plugin-basic-ui";
import { useStepFlow } from "./stackflow";

type ArticleParams = {
  title: string;
};

const Article: ActivityComponentType<ArticleParams> = ({ params }) => {
  // 타입 안정성을 위해 현재 액티비티의 이름을 넣어줘요
  const { stepPush } = useStepFlow("Article");

  const onNextClick = () => {
    // stepPush()을 호출하면 params.title이 변경돼요.
    stepPush({
      title: "Next Title",
    });
  };

  return (
    <AppScreen appBar={{ title: "Article" }}>
      <div>
        <h1>{params.title}</h1>
        <button onClick={onNextClick}>next</button>
      </div>
    </AppScreen>
  );
};

Step 교체하기

const Article: ActivityComponentType<ArticleParams> = ({ params }) => {
  const { stepReplace } = useStepFlow("Article");

  const onChangeClick = () => {
    // stepReplace()을 호출하면 params.title이 변경돼요
    stepReplace({
      title: "Next Title",
    });
  };

  return (
    <AppScreen appBar={{ title: "Article" }}>
      <div>
        <h1>{params.title}</h1>
        <button onClick={onChangeClick}>change</button>
      </div>
    </AppScreen>
  );
};

Step 삭제하기

const Article: ActivityComponentType<ArticleParams> = ({ params }) => {
  const { stepPop } = useStepFlow("Article");

  const onPrevClick = () => {
    // stepPop()을 호출하면 이전 params.title로 돌아가요
    stepPop();
  };

  return (
    <AppScreen appBar={{ title: "Article" }}>
      <div>
        <h1>{params.title}</h1>
        <button onClick={onPrevClick}>prev</button>
      </div>
    </AppScreen>
  );
};

🎨 실제 프로젝트 적용 예제

로그인 관련 화면 구조

📁 프로젝트 구조
activities/
├── act-login.tsx        // Activity
├── act-signup.tsx       // Activity  
├── act-dashboard.tsx    // Activity
└── components/
    ├── login-form.tsx
    ├── password-reset-form.tsx  // Step 내용
    └── find-id-form.tsx         // Step 내용

Activity 구현 예제

// act-login.tsx
import { ActivityComponentType } from "@stackflow/react";
import { AppScreen } from "@stackflow/plugin-basic-ui";
import { useStepFlow } from "../stackflow";
import LoginForm from "../components/login-form";
import PasswordResetForm from "../components/password-reset-form";
import FindIdForm from "../components/find-id-form";

type LoginParams = {
  step?: 'login' | 'resetPassword' | 'findId';
};

const ActLogin: ActivityComponentType<LoginParams> = ({ params }) => {
  const { stepPush, stepPop } = useStepFlow("ActLogin");
  const currentStep = params.step || 'login';

  const showPasswordReset = () => {
    stepPush({ step: 'resetPassword' });
  };

  const showFindId = () => {
    stepPush({ step: 'findId' });
  };

  const goBack = () => {
    stepPop();
  };

  return (
    <AppScreen appBar={{ title: "로그인" }}>
      {currentStep === 'login' && (
        <LoginForm 
          onShowPasswordReset={showPasswordReset}
          onShowFindId={showFindId}
        />
      )}
      {currentStep === 'resetPassword' && (
        <PasswordResetForm onBack={goBack} />
      )}
      {currentStep === 'findId' && (
        <FindIdForm onBack={goBack} />
      )}
    </AppScreen>
  );
};

export default ActLogin;

Activity vs Step 분류 결과

// ✅ Activity로 분류된 화면들
- ActLogin (로그인)
  - 이유: 메인, 보호된 페이지, 만료시 등 여러 곳에서 진입
  - URL: /login

- ActSignup (회원가입)  
  - 이유: 로그인 페이지, 마케팅 페이지 등에서 진입
  - URL: /signup

- ActDashboard (대시보드)
  - 이유: 로그인 후 메인 화면, 독립적 접근 필요
  - URL: /dashboard

// ✅ Step으로 분류된 화면들
- 비밀번호 찾기
  - 이유: 로그인 페이지에서만 진입, URL 없음
  - 구현: ActLogin의 step 파라미터로 관리

- 아이디 찾기
  - 이유: 로그인 페이지에서만 진입, URL 없음  
  - 구현: ActLogin의 step 파라미터로 관리

- 본인인증
  - 이유: 회원가입 과정의 한 단계
  - 구현: ActSignup의 step 파라미터로 관리

🎯 핵심 정리

Activity를 사용해야 할 때

  • 독립적인 URL이 필요한 경우
  • 여러 곳에서 직접 접근해야 하는 경우
  • 브라우저 새로고침 후에도 해당 화면이 유지되어야 하는 경우
  • 앱의 주요 네비게이션 대상인 경우

Step을 사용해야 할 때

  • 특정 Activity의 일부 과정인 경우
  • 하나의 큰 기능을 여러 단계로 나누는 경우
  • 이전/다음 단계의 맥락이 중요한 경우
  • URL 없이 상태로만 관리해도 되는 경우

이 기준을 따르면 Stackflow에서 Activity와 Step을 명확하게 구분하여 사용할 수 있습니다.