<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>B_log</title>
    <link>https://jayb-log.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Fri, 19 Jun 2026 06:00:45 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>B_log</managingEditor>
    <image>
      <title>B_log</title>
      <url>https://tistory1.daumcdn.net/tistory/3876522/attach/86b8e97485474a9195a857d25dcbb79c</url>
      <link>https://jayb-log.tistory.com</link>
    </image>
    <item>
      <title>ChatGPT 성능을 극대화하는 프롬프트 작성법 (개발자/기획자용 가이드)</title>
      <link>https://jayb-log.tistory.com/366</link>
      <description>&lt;h1&gt;ChatGPT 성능을 극대화하는 프롬프트 작성법 (개발자/기획자용 가이드)&lt;/h1&gt;
&lt;p&gt;AI에게 원하는 답변을 정확하게 받기 위해선 &lt;strong&gt;프롬프트(prompt)&lt;/strong&gt;가 핵심입니다.&lt;br&gt;이 글에서는 &lt;strong&gt;ChatGPT의 응답 품질을 높이기 위한 프롬프트 작성 전략&lt;/strong&gt;을 실제 예시와 함께 설명합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;✅ 핵심 요약&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;  목표 명확히 하기&lt;/td&gt;
&lt;td&gt;목적, 기대하는 출력 형태를 명확히&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;  문맥 제공하기&lt;/td&gt;
&lt;td&gt;역할, 대상, 배경 정보를 함께 제시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;  출력 형식 요구&lt;/td&gt;
&lt;td&gt;예시, 마크다운, 표 등 구체적으로 요청&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;  반복 조건 설정&lt;/td&gt;
&lt;td&gt;&amp;quot;예시 3개&amp;quot;, &amp;quot;표로 정리해줘&amp;quot; 등 조건 명시&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;  이전 응답 활용&lt;/td&gt;
&lt;td&gt;&amp;quot;위의 내용을 요약해줘&amp;quot;, &amp;quot;다시 설명해줘&amp;quot; 등&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;h2&gt;  1. 목적을 명확히 기술하라&lt;/h2&gt;
&lt;p&gt;ChatGPT는 질문이 모호하면 일반적인 답변만 제공합니다.&lt;br&gt;&lt;strong&gt;구체적인 목적을 먼저 전달하면 응답의 정확도가 높아집니다.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;❌ Bad Prompt&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Swift 코드 예시 알려줘&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;✅ Good Prompt&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SwiftUI에서 사용자 입력을 받아 텍스트를 실시간으로 반영하는 예시 코드를 알려줘. 텍스트 필드와 라벨을 함께 사용하고, 뷰는 VStack으로 구성해줘.&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2&gt;  2. 역할, 대상, 맥락을 알려줘라&lt;/h2&gt;
&lt;p&gt;AI는 &amp;quot;누구를 위한 답변인지&amp;quot;를 알면 훨씬 유용한 답변을 생성합니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;예시&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;너는 지금부터 3년차 iOS 개발자 면접관이야. 지원자에게 할 수 있는 기술 질문을 5개 만들어줘.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;또는&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;나는 SwiftUI를 배우는 초보자야. 뷰 생명주기를 쉽게 설명해줘.&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2&gt;  3. 출력 형식을 구체적으로 요구하라&lt;/h2&gt;
&lt;p&gt;프롬프트에 원하는 &lt;strong&gt;출력 형식&lt;/strong&gt;을 포함하세요.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;예시 요청&lt;/th&gt;
&lt;th&gt;출력 결과&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&amp;quot;표로 정리해줘&amp;quot;&lt;/td&gt;
&lt;td&gt;마크다운 테이블&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;quot;코드와 함께 설명해줘&amp;quot;&lt;/td&gt;
&lt;td&gt;코드 + 설명&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;quot;단계별로 알려줘&amp;quot;&lt;/td&gt;
&lt;td&gt;1단계, 2단계 형식&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;예시&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Git rebase와 merge의 차이를 표로 정리해줘.&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2&gt;  4. 반복 조건이나 개수를 지정하라&lt;/h2&gt;
&lt;p&gt;특정 &lt;strong&gt;개수, 조건, 반복&lt;/strong&gt; 등을 지정하면 더 정밀한 결과를 얻습니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;예시&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iOS 앱에서 발생할 수 있는 crash 유형 3가지를 알려줘. 각각에 대해 원인과 해결법도 함께 설명해줘.&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2&gt;  5. 이전 응답을 재활용하라&lt;/h2&gt;
&lt;p&gt;이전 대화를 활용하는 것도 좋은 프롬프트 전략입니다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;예시&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;방금 작성한 내용을 블로그용 마크다운으로 다시 만들어줘.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;또는&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;위 예시에 애니메이션을 추가한 버전도 보여줘.&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2&gt;  실전 예시: 프롬프트 비교&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;목적&lt;/th&gt;
&lt;th&gt;Bad Prompt&lt;/th&gt;
&lt;th&gt;Good Prompt&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;디버깅 조언&lt;/td&gt;
&lt;td&gt;앱이 느려요&lt;/td&gt;
&lt;td&gt;SwiftUI 앱에서 List 뷰를 스크롤할 때 버벅임이 발생해. 성능 개선 방법을 알려줘&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;코드 작성&lt;/td&gt;
&lt;td&gt;로그인 화면 만들어줘&lt;/td&gt;
&lt;td&gt;SwiftUI에서 이메일, 비밀번호 입력 필드와 로그인 버튼이 있는 로그인 화면을 만들어줘. Combine을 활용해 유효성 검사도 포함해줘&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;글쓰기&lt;/td&gt;
&lt;td&gt;블로그 글 써줘&lt;/td&gt;
&lt;td&gt;iOS에서 Combine의 debounce 연산자를 설명하는 블로그 글을 마크다운 형식으로 작성해줘&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;h2&gt;  추천 프롬프트 템플릿&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;너는 지금부터 [역할/대상] 이야.
[목적]을 달성하기 위해 필요한 [정보/코드/전략]을 [형식]으로 알려줘.
조건은 [개수, 언어, 예시 등]을 포함해줘.&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;예시&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;너는 지금부터 5년차 iOS 시니어 개발자야.
SwiftUI에서 사용자 입력을 debounce 처리하는 코드를 Combine으로 작성해줘.
주석도 포함하고, 마크다운 코드 블록으로 출력해줘.&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2&gt;✨ 마무리&lt;/h2&gt;
&lt;p&gt;ChatGPT는 똑똑하지만, &lt;strong&gt;명확한 지시 없이 능력을 발휘하긴 어렵습니다.&lt;/strong&gt;&lt;br&gt;프롬프트 작성에 조금만 더 신경 쓴다면, &lt;strong&gt;훨씬 정확하고 고급스러운 결과&lt;/strong&gt;를 받을 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;  추가로 도움이 될 수 있는 프롬프트 예시 모음이나 역할별 프롬프트도 원하시면 언제든지 알려주세요!&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;</description>
      <author>B_log</author>
      <guid isPermaLink="true">https://jayb-log.tistory.com/366</guid>
      <comments>https://jayb-log.tistory.com/366#entry366comment</comments>
      <pubDate>Wed, 9 Jul 2025 15:26:50 +0900</pubDate>
    </item>
    <item>
      <title>Swift로 배우는 전략 패턴 (Strategy Pattern) &amp;ndash; Head First 디자인 패턴 기반 설명</title>
      <link>https://jayb-log.tistory.com/365</link>
      <description>&lt;h1&gt;Swift로 배우는 전략 패턴 (Strategy Pattern) &amp;ndash; Head First 디자인 패턴 기반 설명&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;『Head First Design Patterns』는 디자인 패턴을 쉽고 직관적으로 설명하는 명저입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 그중에서도 &lt;b&gt;전략 패턴(Strategy Pattern)&lt;/b&gt;을 iOS 개발 환경에 맞게 Swift로 구현해보며 학습해보겠습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  전략 패턴이란?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전략 패턴(Strategy Pattern)은 알고리즘(또는 행위)을 객체로 캡슐화하여 &lt;b&gt;상호 교환 가능하게 만드는 디자인 패턴&lt;/b&gt;입니다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 개념:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&quot;행동을 클래스로 분리해서, 실행 중에 바꿀 수 있게 만든다.&quot;&lt;/b&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✅ 전략 패턴 요약 정리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;| 항목 | 내용 |&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;| ------ | ------ |&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;| 패턴명 | 전략(Strategy) 패턴 |&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;| 목적 | 알고리즘을 런타임에 자유롭게 교체 가능하게 만들기 |&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;| 특징 | 1개의 인터페이스 + 여러 전략 구현체 + Context 객체 |&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;| 장점 | OCP 만족, 중복 제거, 행동 교체 가능 |&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;| 자주 쓰이는 경우 | 로그인 방식, 정렬 기준, 렌더링 전략 등 |&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  책 속 예시: 오리 시뮬레이터&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Head First 디자인 패턴에서 소개된 전략 패턴의 대표 예시는 &lt;b&gt;오리 시뮬레이터&lt;/b&gt;입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;code&gt;Duck&lt;/code&gt; 객체는 &lt;code&gt;fly()&lt;/code&gt;와 &lt;code&gt;quack()&lt;/code&gt; 같은 행동을 가짐&lt;/li&gt;
&lt;li&gt;행동은 상황에 따라 달라지므로, 서브클래싱보다 &lt;b&gt;구성(Composition)&lt;/b&gt;을 통해 캡슐화&lt;/li&gt;
&lt;li&gt;&lt;code&gt;FlyBehavior&lt;/code&gt;, &lt;code&gt;QuackBehavior&lt;/code&gt;라는 인터페이스로 행동을 외부에서 주입&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  iOS에서의 실제 예: 로그인 전략&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;iOS 앱에서 다음과 같은 로그인 기능을 제공한다고 가정해봅시다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Apple ID 로그인&lt;/li&gt;
&lt;li&gt;Google 계정 로그인&lt;/li&gt;
&lt;li&gt;게스트 로그인&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 3가지 로그인 방식은 서로 방식이 다르지만, &quot;로그인&quot;이라는 기능은 같기 때문에 전략 패턴이 유용합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. &lt;code&gt;LoginStrategy&lt;/code&gt; 프로토콜 정의&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;
protocol LoginStrategy {

    func login(completion: @escaping (Bool) -&amp;gt; Void)

}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 전략 클래스 구현&lt;/h3&gt;
&lt;pre class=&quot;less&quot;&gt;&lt;code&gt;
struct AppleLoginStrategy: LoginStrategy {

    func login(completion: @escaping (Bool) -&amp;gt; Void) {

        print(&quot;  Apple ID로 로그인 시도&quot;)

        // Apple 로그인 로직

        completion(true)

    }

}



struct GoogleLoginStrategy: LoginStrategy {

    func login(completion: @escaping (Bool) -&amp;gt; Void) {

        print(&quot;  Google 계정으로 로그인 시도&quot;)

        // Google 로그인 로직

        completion(true)

    }

}



struct GuestLoginStrategy: LoginStrategy {

    func login(completion: @escaping (Bool) -&amp;gt; Void) {

        print(&quot;  게스트 로그인&quot;)

        completion(true)

    }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Context 객체: &lt;code&gt;LoginManager&lt;/code&gt;&lt;/h3&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;
final class LoginManager {

    private var strategy: LoginStrategy



    init(strategy: LoginStrategy) {

        self.strategy = strategy

    }



    func setStrategy(_ strategy: LoginStrategy) {

        self.strategy = strategy

    }



    func login(completion: @escaping (Bool) -&amp;gt; Void) {

        strategy.login(completion: completion)

    }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. ViewController에서 사용&lt;/h3&gt;
&lt;pre class=&quot;swift&quot;&gt;&lt;code&gt;
class LoginViewController: UIViewController {

    let loginManager = LoginManager(strategy: GuestLoginStrategy())



    @IBAction func appleLoginTapped() {

        loginManager.setStrategy(AppleLoginStrategy())

        loginManager.login { success in

            print(&quot;로그인 성공 여부: \(success)&quot;)

        }

    }



    @IBAction func googleLoginTapped() {

        loginManager.setStrategy(GoogleLoginStrategy())

        loginManager.login { success in

            print(&quot;로그인 성공 여부: \(success)&quot;)

        }

    }



    @IBAction func guestLoginTapped() {

        loginManager.setStrategy(GuestLoginStrategy())

        loginManager.login { success in

            print(&quot;로그인 성공 여부: \(success)&quot;)

        }

    }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  iOS에서 자주 쓰이는 전략 패턴 예시&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;| 상황 | 전략이 필요한 이유 |&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;| ------ | ------------------- |&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;| 이미지 필터 처리 | 다양한 필터 적용 방식(흑백, 세피아 등)을 런타임에 변경 가능 |&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;| 정렬 기준 선택 | 리스트 정렬 기준을 이름순 / 날짜순 등으로 교체 |&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;| 광고 A/B 전략 | 유저 그룹별 광고 표시 전략 분리 |&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;| 데이터 저장 방식 | UserDefaults, Keychain, File 방식 전략적으로 교체 |&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  고급 팁: 클로저로도 전략을 표현할 수 있다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift에서는 꼭 클래스로만 전략을 구현하지 않아도 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 전략은 &lt;b&gt;클로저&lt;/b&gt;로 대체할 수 있습니다.&lt;/p&gt;
&lt;pre class=&quot;livescript&quot;&gt;&lt;code&gt;
typealias LoginClosure = (@escaping (Bool) -&amp;gt; Void) -&amp;gt; Void



let appleLogin: LoginClosure = { completion in

    print(&quot;  Apple로 로그인&quot;)

    completion(true)

}



let guestLogin: LoginClosure = { completion in

    print(&quot;  게스트로 로그인&quot;)

    completion(true)

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 방식은 간단한 상황에서는 더욱 가볍게 전략을 구성할 수 있어 유용합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전략 패턴은 다음과 같은 경우에 아주 효과적입니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;동작을 바꿔야 하지만, 코드를 수정하고 싶지 않을 때&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;여러 조건에 따라 다른 처리를 해야 할 때&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;테스트 가능성과 코드 확장성을 확보하고 싶을 때&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift와 iOS 프로젝트에서 전략 패턴을 적극적으로 활용해보세요!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한층 더 유연하고 유지보수하기 쉬운 앱을 만들 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;  참고&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;『Head First Design Patterns』, O&amp;rsquo;Reilly Media&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Swift 공식 문서, Apple Developer Guide&lt;/p&gt;
&lt;/blockquote&gt;</description>
      <author>B_log</author>
      <guid isPermaLink="true">https://jayb-log.tistory.com/365</guid>
      <comments>https://jayb-log.tistory.com/365#entry365comment</comments>
      <pubDate>Wed, 9 Jul 2025 15:22:33 +0900</pubDate>
    </item>
    <item>
      <title>Supabase 시작하기: 프론트엔드 개발자를 위한 완전 기초 가이드</title>
      <link>https://jayb-log.tistory.com/363</link>
      <description>&lt;h1&gt;Supabase 시작하기: 프론트엔드 개발자를 위한 완전 기초 가이드&lt;/h1&gt;
&lt;p&gt;Supabase는 오픈소스 Firebase 대체제로, 빠르게 백엔드를 구축하고자 할 때 아주 유용한 도구입니다. 특히 프론트엔드 개발자 입장에서 손쉽게 데이터베이스를 연동하고 인증, 스토리지까지 관리할 수 있어 효율적입니다.&lt;/p&gt;
&lt;h2&gt; ️ Supabase란?&lt;/h2&gt;
&lt;p&gt;Supabase는 다음과 같은 기능들을 제공합니다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;PostgreSQL 기반의 데이터베이스&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;실시간 데이터 변경 감지 (Realtime)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;사용자 인증 및 권한 제어&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;파일 스토리지&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Edge Functions (서버리스 함수)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;  1. Supabase 프로젝트 생성&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://supabase.com&quot;&gt;https://supabase.com&lt;/a&gt; 에 접속해 회원가입 후 로그인&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;New Project&lt;/strong&gt; 클릭&lt;/li&gt;
&lt;li&gt;프로젝트 이름, 비밀번호(Postgres 비밀번호), 지역 등을 입력&lt;/li&gt;
&lt;li&gt;생성 후 &lt;code&gt;Project URL&lt;/code&gt; 과 &lt;code&gt;anon public key&lt;/code&gt; 를 기록해둡니다 (→ 프론트엔드에서 사용)&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;⚙️ 2. 프로젝트에 Supabase 클라이언트 설치&lt;/h2&gt;
&lt;p&gt;React 또는 Vite 기반 프로젝트에서 Supabase를 사용하려면, 다음 패키지를 설치하세요:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install @supabase/supabase-js
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;&lt;strong&gt;  3. Supabase 클라이언트 초기화&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;supabaseClient.js 또는 supabase.js 라는 파일을 만들어 다음과 같이 초기화합니다:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
import { createClient } from &amp;#39;@supabase/supabase-js&amp;#39;

const supabaseUrl = &amp;#39;[https://your-project-id.supabase.co&amp;#39;](https://your-project-id.supabase.co&amp;#39;)  
const supabaseKey = &amp;#39;your-anon-public-key&amp;#39;

export const supabase = createClient(supabaseUrl, supabaseKey)
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;&lt;strong&gt; ️ 4. 테이블 만들기&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Supabase 콘솔에서 Database &amp;gt; Table Editor 에 들어가 직접 테이블을 만들 수 있어요.&lt;/p&gt;
&lt;p&gt;예: todos 테이블&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;237&quot; data-origin-height=&quot;178&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/X9BFN/btsOlikY0It/FzJ1RVynmiKKu1W7PXNYeK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/X9BFN/btsOlikY0It/FzJ1RVynmiKKu1W7PXNYeK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/X9BFN/btsOlikY0It/FzJ1RVynmiKKu1W7PXNYeK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FX9BFN%2FbtsOlikY0It%2FFzJ1RVynmiKKu1W7PXNYeK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;237&quot; height=&quot;178&quot; data-origin-width=&quot;237&quot; data-origin-height=&quot;178&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;✍️ 5. 데이터 가져오기 예시 (React)&lt;/strong&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;
import { useEffect, useState } from &amp;#39;react&amp;#39;  
import { supabase } from &amp;#39;./supabaseClient&amp;#39;

function TodoList() {  
const \[todos, setTodos\] = useState(\[\])

useEffect(() =&amp;gt; {  
supabase  
.from(&amp;#39;todos&amp;#39;)  
.select(&amp;#39;\*&amp;#39;)  
.then(({ data, error }) =&amp;gt; {  
if (error) {  
console.error(error)  
} else {  
setTodos(data)  
}  
})  
}, \[\])

return (

{todos.map((todo) =&amp;gt; (

-   {todo.title}

))}


)  
}
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;&lt;strong&gt;  6. 인증 기능도 기본 제공&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Supabase는 이메일/비밀번호뿐 아니라 소셜 로그인도 지원합니다 (Google, GitHub 등).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;회원가입:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
**const { user, error } = await supabase.auth.signUp({  
email: &amp;#39;user@example.com&amp;#39;,  
password: &amp;#39;secure-password&amp;#39;,  
})**
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;로그인:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
**const { data, error } = await supabase.auth.signInWithPassword({  
email: &amp;#39;user@example.com&amp;#39;,  
password: &amp;#39;secure-password&amp;#39;,  
})**&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;&lt;strong&gt;  7. 실시간 데이터 처리&lt;/strong&gt;&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;
useEffect(() =&amp;gt; {

const subscription = supabase

.channel(&amp;#39;custom-all-channel&amp;#39;)

.on(

  &amp;#39;postgres\_changes&amp;#39;,

  { event: &amp;#39;\*&amp;#39;, schema: &amp;#39;public&amp;#39;, table: &amp;#39;todos&amp;#39; },

  (payload) =&amp;gt; {

    console.log(&amp;#39;변경 감지:&amp;#39;, payload)

  }

)

.subscribe()

return () =&amp;gt; {

supabase.removeChannel(subscription)
}

}, \[\])
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;  8. 스토리지 예시&lt;br&gt;파일 업로드:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;await supabase.storage
  .from(&amp;#39;avatars&amp;#39;)
  .upload(&amp;#39;public/avatar1.png&amp;#39;, file)&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;파일 가져오기 URL:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const { data } = supabase.storage
  .from(&amp;#39;avatars&amp;#39;)
  .getPublicUrl(&amp;#39;public/avatar1.png&amp;#39;)&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;&lt;strong&gt;  마무리&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Supabase는 SQL 기반으로 안정성을 제공하면서도 Firebase처럼 쉽고 빠르게 백엔드를 구축할 수 있는 서비스입니다.&lt;/p&gt;
&lt;p&gt;이 글에서는 다음을 배웠어요:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Supabase 프로젝트 생성&lt;/li&gt;
&lt;li&gt;React와 연동하여 데이터 불러오기&lt;/li&gt;
&lt;li&gt;사용자 인증 및 스토리지 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;&lt;strong&gt;  다음 글 예고&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;다음 글에서는 아래 내용을 다룰 예정입니다:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;실시간 협업 앱 만들기&lt;/li&gt;
&lt;li&gt;RLS(Row Level Security)로 사용자 데이터 보호하기&lt;/li&gt;
&lt;li&gt;Supabase Functions로 서버리스 기능 확장하기&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;이 글이 도움이 되셨다면, 댓글이나 좋아요 부탁드립니다.&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;피드백도 언제든 환영합니다!  &lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;</description>
      <author>B_log</author>
      <guid isPermaLink="true">https://jayb-log.tistory.com/363</guid>
      <comments>https://jayb-log.tistory.com/363#entry363comment</comments>
      <pubDate>Sun, 1 Jun 2025 17:23:44 +0900</pubDate>
    </item>
    <item>
      <title>  React + Tailwind로 드롭다운 만들기 (Dropdown 컴포넌트)</title>
      <link>https://jayb-log.tistory.com/362</link>
      <description>&lt;h1&gt;  React + Tailwind로 드롭다운 만들기 (Dropdown 컴포넌트)&lt;/h1&gt;
&lt;p&gt;이번 글에서는 React와 Tailwind CSS를 사용해&lt;br&gt;&lt;strong&gt;간단하면서도 커스터마이징 가능한 드롭다운(Dropdown) 컴포넌트&lt;/strong&gt;를 만드는 방법을 소개합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;✅ 완성 기능 요약&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;버튼 클릭 시 목록이 아래로 펼쳐짐&lt;/li&gt;
&lt;li&gt;항목 클릭 시 해당 항목 선택 및 드롭다운 닫힘&lt;/li&gt;
&lt;li&gt;선택된 값 표시&lt;/li&gt;
&lt;li&gt;외부 클릭 시 자동 닫힘&lt;/li&gt;
&lt;li&gt;Tailwind CSS로 쉽게 커스터마이징 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;  파일 구조&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;components/
  └── Dropdown.jsx
App.jsx&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2&gt;  1. Dropdown 컴포넌트 (&lt;code&gt;Dropdown.jsx&lt;/code&gt;)&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;import React, { useState, useRef, useEffect } from &amp;#39;react&amp;#39;

function Dropdown({ options = [], placeholder = &amp;#39;선택하세요&amp;#39;, onSelect }) {
  const [isOpen, setIsOpen] = useState(false)
  const [selected, setSelected] = useState(null)
  const dropdownRef = useRef(null)

  const handleSelect = (option) =&amp;gt; {
    setSelected(option)
    setIsOpen(false)
    onSelect?.(option)
  }

  // 바깥 클릭 시 닫힘
  useEffect(() =&amp;gt; {
    function handleClickOutside(e) {
      if (dropdownRef.current &amp;amp;&amp;amp; !dropdownRef.current.contains(e.target)) {
        setIsOpen(false)
      }
    }
    document.addEventListener(&amp;#39;mousedown&amp;#39;, handleClickOutside)
    return () =&amp;gt; document.removeEventListener(&amp;#39;mousedown&amp;#39;, handleClickOutside)
  }, [])

  return (
    &amp;lt;div ref={dropdownRef} className=&amp;quot;relative w-full&amp;quot;&amp;gt;
      &amp;lt;button
        onClick={() =&amp;gt; setIsOpen((prev) =&amp;gt; !prev)}
        className=&amp;quot;w-full px-4 py-3 bg-gray-100 rounded-xl text-left text-sm font-medium text-gray-800 hover:bg-gray-200 transition&amp;quot;
      &amp;gt;
        {selected?.label || placeholder}
      &amp;lt;/button&amp;gt;

      {isOpen &amp;amp;&amp;amp; (
        &amp;lt;ul className=&amp;quot;absolute z-10 mt-2 w-full bg-white shadow-md rounded-xl border border-gray-200&amp;quot;&amp;gt;
          {options.map((option) =&amp;gt; (
            &amp;lt;li
              key={option.value}
              onClick={() =&amp;gt; handleSelect(option)}
              className=&amp;quot;px-4 py-2 text-sm hover:bg-gray-100 cursor-pointer rounded-xl&amp;quot;
            &amp;gt;
              {option.label}
            &amp;lt;/li&amp;gt;
          ))}
        &amp;lt;/ul&amp;gt;
      )}
    &amp;lt;/div&amp;gt;
  )
}

export default Dropdown&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;  2. 사용 예시 (&lt;code&gt;App.jsx&lt;/code&gt;)&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;import { useState } from &amp;#39;react&amp;#39;
import Dropdown from &amp;#39;./components/Dropdown&amp;#39;

function App() {
  const [selectedOption, setSelectedOption] = useState(null)

  const options = [
    { label: &amp;#39;한국어&amp;#39;, value: &amp;#39;ko&amp;#39; },
    { label: &amp;#39;English&amp;#39;, value: &amp;#39;en&amp;#39; },
    { label: &amp;#39;日本語&amp;#39;, value: &amp;#39;ja&amp;#39; },
  ]

  return (
    &amp;lt;div className=&amp;quot;min-h-screen bg-gray-50 flex items-center justify-center p-6&amp;quot;&amp;gt;
      &amp;lt;div className=&amp;quot;w-full max-w-xs space-y-4&amp;quot;&amp;gt;
        &amp;lt;Dropdown
          options={options}
          placeholder=&amp;quot;언어를 선택하세요&amp;quot;
          onSelect={(option) =&amp;gt; {
            setSelectedOption(option)
            console.log(&amp;#39;선택됨:&amp;#39;, option)
          }}
        /&amp;gt;
        {selectedOption &amp;amp;&amp;amp; (
          &amp;lt;p className=&amp;quot;text-sm text-gray-600&amp;quot;&amp;gt;선택한 언어: {selectedOption.label}&amp;lt;/p&amp;gt;
        )}
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}

export default App&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;  커스터마이징 포인트&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;적용 클래스&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;버튼 색상&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bg-gray-100&lt;/code&gt;, &lt;code&gt;hover:bg-gray-200&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;선택 목록 스타일&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hover:bg-gray-100&lt;/code&gt;, &lt;code&gt;text-sm&lt;/code&gt;, &lt;code&gt;rounded-xl&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;위치 조절&lt;/td&gt;
&lt;td&gt;&lt;code&gt;absolute mt-2&lt;/code&gt; → 필요 시 &lt;code&gt;bottom-full&lt;/code&gt; 등&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;h2&gt;✅ 마무리&lt;/h2&gt;
&lt;p&gt;Tailwind CSS와 React만으로도 드롭다운 UI를 충분히 유연하게 구현할 수 있습니다.&lt;br&gt;기본적인 상태 관리와 DOM 이벤트만 다루면, 외부 라이브러리 없이도 실용적인 드롭다운을 만들 수 있죠!&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;  다음 글 예고&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;✅ &lt;code&gt;Select&lt;/code&gt; 컴포넌트를 form과 연결하는 방법&lt;/li&gt;
&lt;li&gt;✅ 항목에 아이콘 추가하기&lt;/li&gt;
&lt;li&gt;✅ 드롭다운을 모달/툴팁으로 확장하기&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;필요하신 기능이 있다면 댓글로 남겨주세요  &lt;/p&gt;</description>
      <author>B_log</author>
      <guid isPermaLink="true">https://jayb-log.tistory.com/362</guid>
      <comments>https://jayb-log.tistory.com/362#entry362comment</comments>
      <pubDate>Sun, 18 May 2025 14:51:55 +0900</pubDate>
    </item>
    <item>
      <title>  아이콘과 텍스트가 함께 들어간 커스터마이징 가능한 버튼 만들기 (Tailwind + React)</title>
      <link>https://jayb-log.tistory.com/361</link>
      <description>&lt;h1&gt;  아이콘과 텍스트가 함께 들어간 커스터마이징 가능한 버튼 만들기 (Tailwind + React)&lt;/h1&gt;
&lt;p&gt;이번 글에서는 Tailwind CSS를 기반으로,&lt;br&gt;&lt;strong&gt;아이콘과 텍스트가 함께 표시되고&lt;/strong&gt;, &lt;strong&gt;색상/폰트/모서리&lt;/strong&gt; 등을 자유롭게 조절할 수 있는 &lt;strong&gt;버튼 컴포넌트&lt;/strong&gt;를 만들어보겠습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;  목표 디자인&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;아이콘 + 텍스트 수평 정렬&lt;/li&gt;
&lt;li&gt;배경색, 텍스트색, border-radius 등 커스터마이징 가능&lt;/li&gt;
&lt;li&gt;클릭 이벤트 핸들링 가능&lt;/li&gt;
&lt;li&gt;반복 사용이 쉬운 재사용형 구조&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;  ButtonPresets.js (스타일 프리셋)&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// src/styles/buttonPresets.js

export const buttonPresets = {
  default: {
    background: &amp;#39;bg-gray-100&amp;#39;,
    textColor: &amp;#39;text-black&amp;#39;,
    font: &amp;#39;text-sm font-semibold&amp;#39;,
    padding: &amp;#39;px-4 py-2&amp;#39;,
    radius: &amp;#39;rounded-xl&amp;#39;,
    iconSize: &amp;#39;w-5 h-5&amp;#39;,
    gap: &amp;#39;gap-2&amp;#39;,
  },
  outline: {
    background: &amp;#39;bg-white border border-gray-300&amp;#39;,
    textColor: &amp;#39;text-gray-700&amp;#39;,
    font: &amp;#39;text-sm font-medium&amp;#39;,
    padding: &amp;#39;px-3 py-2&amp;#39;,
    radius: &amp;#39;rounded-md&amp;#39;,
    iconSize: &amp;#39;w-4 h-4&amp;#39;,
    gap: &amp;#39;gap-1.5&amp;#39;,
  },
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;  IconButton.jsx (컴포넌트 구현)&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;import React from &amp;#39;react&amp;#39;
import { buttonPresets } from &amp;#39;../styles/buttonPresets&amp;#39;

function IconButton({
  icon,
  label,
  onClick,
  variant = &amp;#39;default&amp;#39;,
  customClass = &amp;#39;&amp;#39;,
}) {
  const style = buttonPresets[variant] ?? buttonPresets.default

  return (
    &amp;lt;button
      onClick={onClick}
      className={`
        inline-flex items-center ${style.gap}
        ${style.padding} ${style.background} ${style.textColor} ${style.font} ${style.radius}
        hover:opacity-90 active:scale-95 transition ${customClass}
      `}
    &amp;gt;
      &amp;lt;span className={style.iconSize}&amp;gt;{icon}&amp;lt;/span&amp;gt;
      &amp;lt;span&amp;gt;{label}&amp;lt;/span&amp;gt;
    &amp;lt;/button&amp;gt;
  )
}

export default IconButton&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;  사용 예시 (App.jsx)&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;import { Pencil, Share2 } from &amp;#39;lucide-react&amp;#39;
import IconButton from &amp;#39;./components/IconButton&amp;#39;

function App() {
  return (
    &amp;lt;div className=&amp;quot;flex gap-4 p-6 bg-gray-50&amp;quot;&amp;gt;
      &amp;lt;IconButton
        icon={&amp;lt;Pencil /&amp;gt;}
        label=&amp;quot;수정하기&amp;quot;
        onClick={() =&amp;gt; alert(&amp;#39;수정 클릭&amp;#39;)}
        variant=&amp;quot;default&amp;quot;
      /&amp;gt;
      &amp;lt;IconButton
        icon={&amp;lt;Share2 /&amp;gt;}
        label=&amp;quot;공유하기&amp;quot;
        onClick={() =&amp;gt; alert(&amp;#39;공유 클릭&amp;#39;)}
        variant=&amp;quot;default&amp;quot;
      /&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;p&gt;아이콘은 &lt;code&gt;lucide-react&lt;/code&gt; 또는 &lt;code&gt;@heroicons/react&lt;/code&gt; 등 사용 가능&lt;br&gt;설치: &lt;code&gt;npm install lucide-react&lt;/code&gt;&lt;/p&gt;
&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;hr&gt;
&lt;h2&gt;  확장 아이디어&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;버튼에 &lt;code&gt;disabled&lt;/code&gt; 속성 추가&lt;/li&gt;
&lt;li&gt;&lt;code&gt;iconPosition=&amp;quot;left&amp;quot; | &amp;quot;right&amp;quot;&lt;/code&gt; 으로 아이콘 위치 바꾸기&lt;/li&gt;
&lt;li&gt;색상 preset을 &lt;code&gt;primary&lt;/code&gt;, &lt;code&gt;danger&lt;/code&gt;, &lt;code&gt;success&lt;/code&gt; 등으로 분리&lt;/li&gt;
&lt;li&gt;버튼 크기 preset (&lt;code&gt;sm&lt;/code&gt;, &lt;code&gt;md&lt;/code&gt;, &lt;code&gt;lg&lt;/code&gt;) 도입&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;✅ 마무리&lt;/h2&gt;
&lt;p&gt;이처럼 버튼을 아이콘 + 텍스트 조합으로 만들고,&lt;br&gt;Tailwind 스타일을 preset으로 관리하면 &lt;strong&gt;재사용성과 유지보수가 훨씬 쉬운 구조&lt;/strong&gt;가 됩니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;  다음 글 예고&lt;/h2&gt;
&lt;p&gt;다음 글에서는 &lt;strong&gt;버튼 그룹 구성&lt;/strong&gt;, 또는 &lt;strong&gt;로딩 상태가 있는 버튼 (로딩 스피너 포함)&lt;/strong&gt; 컴포넌트를 소개할 예정입니다.&lt;br&gt;필요한 요소가 있다면 댓글로 남겨주세요. 감사합니다  &lt;/p&gt;</description>
      <author>B_log</author>
      <guid isPermaLink="true">https://jayb-log.tistory.com/361</guid>
      <comments>https://jayb-log.tistory.com/361#entry361comment</comments>
      <pubDate>Sun, 18 May 2025 14:20:03 +0900</pubDate>
    </item>
    <item>
      <title>  TextField 컴포넌트 사용 예시 (React + Tailwind)</title>
      <link>https://jayb-log.tistory.com/360</link>
      <description>&lt;h1&gt;  TextField 컴포넌트 사용 예시 (React + Tailwind)&lt;/h1&gt;
&lt;p&gt;앞서 만든 텍스트 필드를 실제 프로젝트에서 어떻게 사용하는지 보여드리겠습니다.&lt;br&gt;아래는 &lt;code&gt;App.jsx&lt;/code&gt; 또는 특정 페이지 컴포넌트에서 사용할 수 있는 예시입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;  1. TextField 컴포넌트 만들기 (&lt;code&gt;TextField.jsx&lt;/code&gt;)&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;import React from &amp;#39;react&amp;#39;

function TextField({
  placeholder = &amp;#39;텍스트를 입력하세요&amp;#39;,
  value,
  onChange,
  fontClass = &amp;#39;text-base font-medium&amp;#39;,
  borderClass = &amp;#39;border border-gray-200 rounded-xl&amp;#39;,
  paddingClass = &amp;#39;px-4 py-3&amp;#39;,
}) {
  return (
    &amp;lt;input
      type=&amp;quot;text&amp;quot;
      value={value}
      onChange={onChange}
      placeholder={placeholder}
      className={`
        w-full bg-white placeholder-gray-400
        ${fontClass} ${borderClass} ${paddingClass}
        focus:outline-none focus:ring-2 focus:ring-blue-400 transition
      `}
    /&amp;gt;
  )
}

export default TextField&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;  2. 사용하는 예시 (&lt;code&gt;App.jsx&lt;/code&gt;)&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;import { useState } from &amp;#39;react&amp;#39;
import TextField from &amp;#39;./TextField&amp;#39;

function App() {
  const [title, setTitle] = useState(&amp;#39;&amp;#39;)

  return (
    &amp;lt;div className=&amp;quot;min-h-screen bg-gray-50 flex items-center justify-center p-6&amp;quot;&amp;gt;
      &amp;lt;div className=&amp;quot;w-full max-w-xl space-y-4&amp;quot;&amp;gt;
        &amp;lt;label className=&amp;quot;text-gray-700 font-semibold text-sm&amp;quot;&amp;gt;
          모임 제목을 입력해주세요
        &amp;lt;/label&amp;gt;

        &amp;lt;TextField
          placeholder=&amp;quot;예: 삼겹살 파티, 디자인 회식&amp;quot;
          value={title}
          onChange={(e) =&amp;gt; setTitle(e.target.value)}
          fontClass=&amp;quot;text-lg font-semibold&amp;quot;
          borderClass=&amp;quot;border border-gray-200 rounded-xl&amp;quot;
          paddingClass=&amp;quot;px-5 py-3&amp;quot;
        /&amp;gt;

        &amp;lt;div className=&amp;quot;text-sm text-gray-500&amp;quot;&amp;gt;입력된 제목: {title}&amp;lt;/div&amp;gt;
      &amp;lt;/div&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}

export default App&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;✅ 결과 UI 특징&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;깔끔하고 둥근 테두리&lt;/li&gt;
&lt;li&gt;폰트 사이즈와 굵기 조절 가능&lt;/li&gt;
&lt;li&gt;placeholder는 흐릿하게 표시&lt;/li&gt;
&lt;li&gt;포커스 시 파란 테두리(&lt;code&gt;ring-blue-400&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;입력 결과는 실시간 아래에 표시&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;  커스터마이징 팁&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;커스터마이징 요소&lt;/th&gt;
&lt;th&gt;바꾸는 prop&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;입력창 크기&lt;/td&gt;
&lt;td&gt;&lt;code&gt;w-full&lt;/code&gt; 또는 부모 div의 &lt;code&gt;max-w&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;텍스트 크기&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fontClass=&amp;quot;text-sm&amp;quot;&lt;/code&gt; 등&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;여백 조정&lt;/td&gt;
&lt;td&gt;&lt;code&gt;paddingClass=&amp;quot;px-3 py-2&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;테두리 강조&lt;/td&gt;
&lt;td&gt;&lt;code&gt;borderClass=&amp;quot;border border-blue-500&amp;quot;&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;p&gt;이제 &lt;code&gt;TextField&lt;/code&gt;를 다양한 입력값에 맞게 자유롭게 확장해서 사용할 수 있습니다!&lt;br&gt;필요하시면 &lt;strong&gt;Form 전체 구성&lt;/strong&gt;, &lt;strong&gt;유효성 검사&lt;/strong&gt;, &lt;strong&gt;에러 메시지 표시&lt;/strong&gt; 등도 이어서 도와드릴게요  &lt;/p&gt;</description>
      <author>B_log</author>
      <guid isPermaLink="true">https://jayb-log.tistory.com/360</guid>
      <comments>https://jayb-log.tistory.com/360#entry360comment</comments>
      <pubDate>Sun, 18 May 2025 13:41:47 +0900</pubDate>
    </item>
    <item>
      <title>✏️ Tailwind CSS로 커스터마이징 가능한 텍스트 필드 만들기</title>
      <link>https://jayb-log.tistory.com/359</link>
      <description>&lt;h1&gt;✏️ Tailwind CSS로 커스터마이징 가능한 텍스트 필드 만들기&lt;/h1&gt;
&lt;p&gt;입력 필드(UI Text Field)는 모든 사용자 인터페이스에서 가장 많이 쓰이는 컴포넌트입니다.&lt;br&gt;이번 글에서는 Tailwind CSS를 이용해 placeholder, 폰트, 여백, 테두리 등을 자유롭게 조정할 수 있는 &lt;strong&gt;커스터마이징 가능한 텍스트 입력 필드&lt;/strong&gt;를 만드는 방법을 소개합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;✅ 기본적인 입력 필드 구성&lt;/h2&gt;
&lt;p&gt;Tailwind로 텍스트 필드를 구성할 때 고려해야 할 주요 요소는 다음과 같습니다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;항목&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;th&gt;Tailwind 예시&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;크기&lt;/td&gt;
&lt;td&gt;너비 및 높이&lt;/td&gt;
&lt;td&gt;&lt;code&gt;w-full&lt;/code&gt;, &lt;code&gt;h-12&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;여백&lt;/td&gt;
&lt;td&gt;내부 패딩&lt;/td&gt;
&lt;td&gt;&lt;code&gt;px-4&lt;/code&gt;, &lt;code&gt;py-3&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;테두리&lt;/td&gt;
&lt;td&gt;굵기, 둥근 정도&lt;/td&gt;
&lt;td&gt;&lt;code&gt;border&lt;/code&gt;, &lt;code&gt;rounded-lg&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;폰트&lt;/td&gt;
&lt;td&gt;크기, 굵기&lt;/td&gt;
&lt;td&gt;&lt;code&gt;text-base&lt;/code&gt;, &lt;code&gt;font-medium&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;상태&lt;/td&gt;
&lt;td&gt;포커스 시 효과&lt;/td&gt;
&lt;td&gt;&lt;code&gt;focus:outline-none&lt;/code&gt;, &lt;code&gt;focus:ring&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;placeholder&lt;/td&gt;
&lt;td&gt;글자 색상 조정&lt;/td&gt;
&lt;td&gt;&lt;code&gt;placeholder-gray-400&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;h2&gt;  예제 코드: 커스터마이징 가능한 텍스트 필드&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;function TextField({
  placeholder = &amp;#39;텍스트를 입력하세요&amp;#39;,
  value,
  onChange,
  fontClass = &amp;#39;text-base font-medium&amp;#39;,
  borderClass = &amp;#39;border border-gray-200 rounded-xl&amp;#39;,
  paddingClass = &amp;#39;px-4 py-3&amp;#39;,
}) {
  return (
    &amp;lt;input
      type=&amp;quot;text&amp;quot;
      value={value}
      onChange={onChange}
      placeholder={placeholder}
      className={`
        w-full bg-white placeholder-gray-400
        ${fontClass} ${borderClass} ${paddingClass}
        focus:outline-none focus:ring-2 focus:ring-blue-400 transition
      `}
    /&amp;gt;
  )
}&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;  사용 예시&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;&amp;lt;TextField
  placeholder=&amp;quot;삼겹살 파티, 디자인 회식&amp;quot;
  value={title}
  onChange={(e) =&amp;gt; setTitle(e.target.value)}
  fontClass=&amp;quot;text-lg font-semibold&amp;quot;
  borderClass=&amp;quot;border border-gray-200 rounded-xl&amp;quot;
  paddingClass=&amp;quot;px-5 py-3&amp;quot;
/&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;✨ 확장 아이디어&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;disabled&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt; 상태를 위한 클래스 조건부 적용&lt;/li&gt;
&lt;li&gt;  아이콘이 포함된 텍스트 필드 (왼쪽 or 오른쪽)&lt;/li&gt;
&lt;li&gt;  label, 설명 텍스트와 함께 묶는 form-group 구성&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;✅ 마무리&lt;/h2&gt;
&lt;p&gt;Tailwind CSS는 &lt;code&gt;클래스 조합&lt;/code&gt;만으로도 매우 유연하게 텍스트 필드를 구성할 수 있습니다.&lt;br&gt;컴포넌트화해 두면 다양한 스타일, 폰트, 테두리 형태로 손쉽게 커스터마이징이 가능하므로, 프로젝트마다 반복적으로 쓰이는 입력창을 효율적으로 관리할 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;  다음 글 예고&lt;/h2&gt;
&lt;p&gt;다음 글에서는 &lt;strong&gt;아이콘이 포함된 텍스트 필드&lt;/strong&gt;, 또는 &lt;strong&gt;에러 메시지와 연동된 인풋 필드 상태 처리&lt;/strong&gt; 방법을 소개할 예정입니다.&lt;br&gt;원하시는 확장 방향이 있다면 댓글로 알려주세요!  &lt;/p&gt;</description>
      <author>B_log</author>
      <guid isPermaLink="true">https://jayb-log.tistory.com/359</guid>
      <comments>https://jayb-log.tistory.com/359#entry359comment</comments>
      <pubDate>Sun, 18 May 2025 13:30:57 +0900</pubDate>
    </item>
    <item>
      <title>  Tailwind로 만드는 이미지 버튼 컴포넌트 (선택 테두리 + 커스텀 X버튼 위치)</title>
      <link>https://jayb-log.tistory.com/358</link>
      <description>&lt;h1&gt;  Tailwind로 만드는 이미지 버튼 컴포넌트 (선택 테두리 + 커스텀 X버튼 위치)&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 글에서는 다음 기능을 가진 UI 컴포넌트를 구현하는 기초 지식을 소개합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이미지를 표시하는 사각형 버튼&lt;/li&gt;
&lt;li&gt;클릭 시 선택됨 상태로 전환되며 border 표시&lt;/li&gt;
&lt;li&gt;X 버튼은 오른쪽 상단에 위치하며, 위치는 자유롭게 조정 가능&lt;/li&gt;
&lt;li&gt;이미지 교체가 가능하도록 props 기반 설계&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✅ 컴포넌트 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;React 기준으로 아래와 같은 구조로 구성할 수 있습니다:&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  핵심 기능&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;기능&lt;/th&gt;
&lt;th&gt;구현 요소&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;선택 여부&lt;/td&gt;
&lt;td&gt;&lt;code&gt;selected&lt;/code&gt; prop으로 제어&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;이미지 경로&lt;/td&gt;
&lt;td&gt;&lt;code&gt;imageSrc&lt;/code&gt; prop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;X 버튼 위치&lt;/td&gt;
&lt;td&gt;&lt;code&gt;closeButtonPosition&lt;/code&gt; prop (&lt;code&gt;absolute&lt;/code&gt; + Tailwind 조합)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;클릭 핸들러&lt;/td&gt;
&lt;td&gt;&lt;code&gt;onClick&lt;/code&gt;, &lt;code&gt;onClose&lt;/code&gt; 분리&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;✅ 예제 코드&lt;/h2&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;import React from 'react'
import { X } from 'lucide-react' // X 아이콘 (lucide-react 또는 HeroIcons 사용 가능)

function ImageSelectButton({
  imageSrc,
  selected = false,
  onClick,
  onClose,
  closeButtonPosition = 'top-1 right-1',
}) {
  return (
    &amp;lt;div
      onClick={onClick}
      className={`relative w-20 h-20 rounded-lg flex items-center justify-center
        ${selected ? 'border-4 border-blue-500' : 'border border-transparent'}
        bg-yellow-200 hover:cursor-pointer transition`}
    &amp;gt;
      &amp;lt;img src={imageSrc} alt=&quot;선택 아이콘&quot; className=&quot;w-10 h-10&quot; /&amp;gt;

      {onClose &amp;amp;&amp;amp; (
        &amp;lt;button
          onClick={(e) =&amp;gt; {
            e.stopPropagation()
            onClose()
          }}
          className={`absolute ${closeButtonPosition} bg-white rounded-full p-1 shadow-md`}
        &amp;gt;
          &amp;lt;X size={12} /&amp;gt;
        &amp;lt;/button&amp;gt;
      )}
    &amp;lt;/div&amp;gt;
  )
}

export default ImageSelectButton&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  사용 예시&lt;/h2&gt;
&lt;pre class=&quot;typescript&quot;&gt;&lt;code&gt;&amp;lt;ImageSelectButton
  imageSrc=&quot;/icons/rice.png&quot;
  selected={true}
  onClick={() =&amp;gt; console.log('카테고리 선택')}
  onClose={() =&amp;gt; console.log('X 클릭됨')}
  closeButtonPosition=&quot;top-2 right-2&quot;
/&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  마무리&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 방식으로 컴포넌트를 구성하면 다음과 같은 이점이 있습니다:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;다양한 이미지를 props로 교체 가능&lt;/li&gt;
&lt;li&gt;상태에 따라 border 스타일링 변경&lt;/li&gt;
&lt;li&gt;닫기 버튼의 위치를 Tailwind 클래스 조합으로 자유롭게 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞으로 이 컴포넌트를 기반으로 &lt;b&gt;카테고리 선택 그리드&lt;/b&gt;, &lt;b&gt;삭제 가능한 태그 목록&lt;/b&gt;, &lt;b&gt;커스텀 갤러리&lt;/b&gt; 등 다양한 형태로 확장할 수 있습니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  참고&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;X 아이콘은 &lt;a href=&quot;https://lucide.dev/icons/x&quot;&gt;lucide-react&lt;/a&gt; 또는 &lt;code&gt;@heroicons/react&lt;/code&gt; 패키지를 추천합니다.&lt;/li&gt;
&lt;li&gt;Tailwind의 &lt;code&gt;absolute&lt;/code&gt;, &lt;code&gt;top-1&lt;/code&gt;, &lt;code&gt;right-1&lt;/code&gt; 조합을 활용하면 버튼 위치를 자유롭게 제어할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>B_log</author>
      <guid isPermaLink="true">https://jayb-log.tistory.com/358</guid>
      <comments>https://jayb-log.tistory.com/358#entry358comment</comments>
      <pubDate>Sat, 17 May 2025 22:51:36 +0900</pubDate>
    </item>
    <item>
      <title>✨ Tailwind CSS로 UI를 만들기 전에 꼭 알아야 할 기초 컴포넌트 개념</title>
      <link>https://jayb-log.tistory.com/357</link>
      <description>&lt;h1&gt;✨ Tailwind CSS로 UI를 만들기 전에 꼭 알아야 할 기초 컴포넌트 개념&lt;/h1&gt;
&lt;p&gt;프론트엔드 화면을 디자인하다 보면, 자주 반복되는 UI 요소들이 있습니다.&lt;br&gt;Tailwind CSS를 사용할 때 이 요소들을 빠르게 그리고 일관되게 만들기 위해서는 몇 가지 &lt;strong&gt;기초 개념&lt;/strong&gt;을 이해하고 있는 것이 중요합니다.&lt;/p&gt;
&lt;p&gt;이번 글에서는 특히 &lt;strong&gt;버튼&lt;/strong&gt;, &lt;strong&gt;입력 필드&lt;/strong&gt;, &lt;strong&gt;텍스트&lt;/strong&gt;, &lt;strong&gt;간격 및 정렬&lt;/strong&gt; 등 실제 UI를 구성할 때 꼭 알아야 할 Tailwind CSS의 핵심 요소들을 소개합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;  버튼(Button) 컴포넌트&lt;/h2&gt;
&lt;p&gt;버튼은 거의 모든 웹 UI에서 사용되는 핵심 요소입니다.&lt;br&gt;Tailwind에서는 유틸리티 클래스 조합을 통해 다양한 버튼 스타일을 직접 만들 수 있습니다.&lt;/p&gt;
&lt;h3&gt;✅ 버튼 구성의 기본 요소&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;요소&lt;/th&gt;
&lt;th&gt;예시 클래스&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;배경색&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bg-blue-500&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;버튼 색상&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;텍스트색&lt;/td&gt;
&lt;td&gt;&lt;code&gt;text-white&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;글자 색상&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;여백&lt;/td&gt;
&lt;td&gt;&lt;code&gt;px-4 py-2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;버튼 크기 조절&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;테두리&lt;/td&gt;
&lt;td&gt;&lt;code&gt;rounded-md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;둥근 모서리&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;상태&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hover:bg-blue-600&lt;/code&gt;, &lt;code&gt;disabled:opacity-50&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;상호작용 효과&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;✅ 버튼 기본 예시&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;button class=&amp;quot;bg-blue-500 text-white px-4 py-2 rounded-md hover:bg-blue-600 transition&amp;quot;&amp;gt;
  버튼 텍스트
&amp;lt;/button&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;  텍스트 필드(Input) 컴포넌트&lt;/h2&gt;
&lt;p&gt;사용자 입력을 받을 수 있는 입력창은 &lt;strong&gt;가독성&lt;/strong&gt;, &lt;strong&gt;포커스 효과&lt;/strong&gt;, &lt;strong&gt;테두리 스타일&lt;/strong&gt; 등이 중요합니다.&lt;/p&gt;
&lt;h3&gt;✅ 입력창 구성의 기본 요소&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;요소&lt;/th&gt;
&lt;th&gt;예시 클래스&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;테두리&lt;/td&gt;
&lt;td&gt;&lt;code&gt;border border-gray-300&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;기본 입력창 느낌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;여백&lt;/td&gt;
&lt;td&gt;&lt;code&gt;p-3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;텍스트 입력 간격 확보&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;반응형 효과&lt;/td&gt;
&lt;td&gt;&lt;code&gt;focus:outline-none&lt;/code&gt;, &lt;code&gt;focus:ring-2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;포커스 시 강조&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;전체 너비&lt;/td&gt;
&lt;td&gt;&lt;code&gt;w-full&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;한 줄 전체 차지&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;✅ 입력 필드 예시&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;input
  type=&amp;quot;text&amp;quot;
  placeholder=&amp;quot;예시 문구&amp;quot;
  class=&amp;quot;w-full border border-gray-300 rounded-md p-3 text-sm focus:outline-none focus:ring-2 focus:ring-blue-400&amp;quot;
/&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;  간격 (Spacing) &amp;amp; 정렬 (Layout)&lt;/h2&gt;
&lt;p&gt;Tailwind에서 UI를 정렬하고 배치하는 것은 매우 직관적입니다.&lt;/p&gt;
&lt;h3&gt;✅ 간격 클래스&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;클래스&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;p-4&lt;/code&gt;, &lt;code&gt;px-6&lt;/code&gt;, &lt;code&gt;py-2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;내부 여백 (padding)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;m-2&lt;/code&gt;, &lt;code&gt;mt-4&lt;/code&gt;, &lt;code&gt;mb-1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;외부 여백 (margin)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;gap-4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;flex/grid 자식 간의 간격&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;✅ 정렬 클래스&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;클래스&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;flex&lt;/code&gt;, &lt;code&gt;grid&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;레이아웃 시작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;justify-center&lt;/code&gt;, &lt;code&gt;items-center&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;정렬 방향 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;space-x-4&lt;/code&gt;, &lt;code&gt;space-y-2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;요소 사이 간격 설정&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;h2&gt;  텍스트 스타일링&lt;/h2&gt;
&lt;p&gt;Tailwind에서는 텍스트 크기, 두께, 색상도 클래스 하나로 쉽게 조절할 수 있습니다.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;클래스&lt;/th&gt;
&lt;th&gt;설명&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;text-sm&lt;/code&gt;, &lt;code&gt;text-lg&lt;/code&gt;, &lt;code&gt;text-2xl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;글자 크기&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;font-bold&lt;/code&gt;, &lt;code&gt;font-semibold&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;글자 두께&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;text-gray-700&lt;/code&gt;, &lt;code&gt;text-blue-500&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;글자 색상&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;hr&gt;
&lt;h2&gt;✅ 실무 팁: 컴포넌트 기준으로 사고하자&lt;/h2&gt;
&lt;p&gt;Tailwind는 &lt;strong&gt;CSS를 작성하지 않고도 컴포넌트를 바로 만들 수 있는 도구&lt;/strong&gt;입니다.&lt;br&gt;따라서 버튼, 입력창, 카드, 라벨 같은 요소들을 “컴포넌트 단위로 구상하고 클래스 조합으로 스타일링”하는 연습이 중요합니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;  마무리&lt;/h2&gt;
&lt;p&gt;Tailwind CSS를 이용한 UI 구성은 클래스 몇 줄로도 충분히 세련되고 일관된 디자인을 만들 수 있다는 게 가장 큰 장점입니다.&lt;br&gt;이번 글에서 소개한 기초 요소들을 잘 익히면, 어떤 UI든 빠르게 구축할 수 있습니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;  다음 글 예고&lt;/h2&gt;
&lt;p&gt;다음 글에서는 실제 UI 흐름 속에서 이 컴포넌트들을 어떻게 배치하고 조합하는지,&lt;br&gt;&lt;strong&gt;레이아웃을 구성하는 실전 예제&lt;/strong&gt;를 소개할 예정입니다.&lt;/p&gt;
&lt;p&gt;궁금한 점이나 원하는 주제가 있다면 댓글로 남겨주세요. 감사합니다  &lt;/p&gt;</description>
      <author>B_log</author>
      <guid isPermaLink="true">https://jayb-log.tistory.com/357</guid>
      <comments>https://jayb-log.tistory.com/357#entry357comment</comments>
      <pubDate>Sat, 17 May 2025 22:36:32 +0900</pubDate>
    </item>
    <item>
      <title>  React + Vite에 Tailwind CSS 적용하는 방법</title>
      <link>https://jayb-log.tistory.com/356</link>
      <description>&lt;h1&gt;  React + Vite에 Tailwind CSS 적용하는 방법&lt;/h1&gt;
&lt;p&gt;안녕하세요! 지난 글에서 Tailwind CSS가 왜 좋은지에 대해 알아봤죠?&lt;br&gt;이번 글에서는 실제로 &lt;strong&gt;React + Vite 프로젝트에 Tailwind CSS를 설치하고 적용하는 방법&lt;/strong&gt;을 단계별로 정리해드릴게요.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;✅ 1단계: 프로젝트 준비&lt;/h2&gt;
&lt;p&gt;먼저 Vite + React 프로젝트가 준비되어 있어야 합니다.&lt;br&gt;아직 생성하지 않으셨다면 아래 명령어로 먼저 프로젝트를 만들고 시작하세요.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;npm create vite@latest my-app -- --template react
cd my-app
npm install&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;✅ 2단계: Tailwind CSS 설치&lt;/h2&gt;
&lt;p&gt;Tailwind CSS 관련 패키지를 설치합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;위 명령어는 &lt;code&gt;tailwind.config.js&lt;/code&gt;와 &lt;code&gt;postcss.config.js&lt;/code&gt; 파일을 생성합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;✅ 3단계: Tailwind 설정 수정&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;tailwind.config.js&lt;/code&gt; 파일을 열고 &lt;code&gt;content&lt;/code&gt; 항목을 아래처럼 수정합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// tailwind.config.js
export default {
  content: [
    &amp;quot;./index.html&amp;quot;,
    &amp;quot;./src/**/*.{js,ts,jsx,tsx}&amp;quot;,
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;이 설정은 Tailwind가 사용할 클래스들을 자동으로 감지할 수 있게 해줍니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;✅ 4단계: 스타일 파일 생성 및 연결&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;src/index.css&lt;/code&gt; 파일을 만들고 아래와 같이 작성합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;/* src/index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;그 다음, &lt;code&gt;src/main.jsx&lt;/code&gt; (또는 main.tsx)에 이 CSS 파일을 import 합니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// src/main.jsx
import React from &amp;#39;react&amp;#39;
import ReactDOM from &amp;#39;react-dom/client&amp;#39;
import App from &amp;#39;./App.jsx&amp;#39;
import &amp;#39;./index.css&amp;#39;

ReactDOM.createRoot(document.getElementById(&amp;#39;root&amp;#39;)).render(
  &amp;lt;React.StrictMode&amp;gt;
    &amp;lt;App /&amp;gt;
  &amp;lt;/React.StrictMode&amp;gt;
)&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;✅ 5단계: 제대로 적용됐는지 확인&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;App.jsx&lt;/code&gt;에 Tailwind 클래스를 추가해봅니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-jsx&quot;&gt;// src/App.jsx
function App() {
  return (
    &amp;lt;div className=&amp;quot;min-h-screen flex items-center justify-center bg-gradient-to-r from-blue-500 to-purple-600 text-white text-3xl font-bold&amp;quot;&amp;gt;
      Hello Tailwind CSS!
    &amp;lt;/div&amp;gt;
  )
}

export default App&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;✅ 6단계: 개발 서버 실행&lt;/h2&gt;
&lt;p&gt;이제 개발 서버를 다시 실행해봅니다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;npm run dev&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;브라우저에 Tailwind 스타일이 적용된 화면이 뜬다면 성공입니다!&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;  마무리&lt;/h2&gt;
&lt;p&gt;이제 여러분의 React + Vite 프로젝트에 Tailwind CSS가 성공적으로 적용되었습니다!&lt;br&gt;앞으로는 복잡한 CSS 파일 없이, HTML 클래스만으로도 멋진 UI를 빠르게 만들 수 있어요.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;  다음 글 예고&lt;/h2&gt;
&lt;p&gt;다음 포스팅에서는 &lt;strong&gt;Tailwind CSS로 자주 사용하는 UI 구성 요소(버튼, 카드, 폼 등)를 빠르게 만드는 방법&lt;/strong&gt;을 소개해드릴게요.&lt;br&gt;지금까지 따라오시느라 고생 많으셨고, 궁금한 점은 댓글로 남겨주세요!  &lt;/p&gt;</description>
      <author>B_log</author>
      <guid isPermaLink="true">https://jayb-log.tistory.com/356</guid>
      <comments>https://jayb-log.tistory.com/356#entry356comment</comments>
      <pubDate>Sat, 17 May 2025 22:07:11 +0900</pubDate>
    </item>
  </channel>
</rss>