<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>path_creator.log</title>
        <link>https://velog.io/</link>
        <description>개발자+분석가+BusinessStrategist</description>
        <lastBuildDate>Wed, 16 Jul 2025 05:41:03 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>path_creator.log</title>
            <url>https://velog.velcdn.com/images/path_creator/profile/4ed40264-c7aa-4063-9307-7582823602af/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. path_creator.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/path_creator" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[전략적인 uv 구성과 적용 방법]]></title>
            <link>https://velog.io/@path_creator/%EC%A0%84%EB%9E%B5%EC%A0%81%EC%9D%B8-uv-%EA%B5%AC%EC%84%B1%EA%B3%BC-%EC%A0%81%EC%9A%A9-%EB%B0%A9%EB%B2%95</link>
            <guid>https://velog.io/@path_creator/%EC%A0%84%EB%9E%B5%EC%A0%81%EC%9D%B8-uv-%EA%B5%AC%EC%84%B1%EA%B3%BC-%EC%A0%81%EC%9A%A9-%EB%B0%A9%EB%B2%95</guid>
            <pubDate>Wed, 16 Jul 2025 05:41:03 GMT</pubDate>
            <description><![CDATA[<h2 id="intro">Intro</h2>
<p>요즘 fastapi 개발 전략중 <strong>uv</strong>가 <strong>requirements.txt</strong>보다 편리한걸로 뽑힌다.
이번 블로그 글에선 간단하게 패키지를 관리하는 방법을 보여주겠다.</p>
<p>*<em>진심이다 ㅋ : *</em>아직 나보다 간단하고 전략적으로 구성하는 사람이 없는거 같아서 내글을 남긴다.</p>
<p><strong>이유 :</strong></p>
<ul>
<li><p>성능(Performance) :
Rust 기반 Resolver로 의존성 설치 속도가 requirements.txt나 poetry 대비 10~100배 빠르다는 공식 벤치마크가 있습니다.
캐싱과 최적화를 적극 활용하여 대규모 프로젝트에서도 빠른 설치 및 업데이트가 가능합니다.</p>
</li>
<li><p>사용 편의성(Usability) :
uv add 등 단순화된 CLI 명령어로 대부분의 작업을 수행할 수 있습니다.
.venv 폴더나 파이썬 버전 설정(.python-version 등)을 직접 신경 쓰지 않아도 자동으로 생성·관리해줍니다.</p>
</li>
</ul>
<h2 id="들어가기-앞서-구성해야-될-것">들어가기 앞서 구성해야 될 것</h2>
<p><strong>requirements의 구성을 목적별로 나누어 구성</strong>
<img src="https://velog.velcdn.com/images/path_creator/post/18e12233-c53f-46e3-a840-3c6e15b14ac5/image.png" alt=""></p>
<p>아래는 제 requirements.txt 안에 내용에선 용도마다 개념 주석을 씁니다.
<img src="https://velog.velcdn.com/images/path_creator/post/aac337b2-e412-4ae9-8242-c574929b883f/image.png" alt=""></p>
<h2 id="패키지-설치와-적용-순서">패키지 설치와 적용 순서</h2>
<p><strong>1. 프로젝트 생성</strong></p>
<blockquote>
<p>pip install uv 
위와같이 설치 후, 아래를 이행</p>
</blockquote>
<blockquote>
<p>uv init &lt;프로젝트 이름&gt;</p>
</blockquote>
<ul>
<li>아래처럼 구성되게 된다.</li>
</ul>
<p>&lt;프로젝트 이름&gt;/
├── .python-version
├── main.py
├── pyproject.toml
└── README.md</p>
<p>또는 *<em>프로젝트가 이미 있다면 *</em>프로젝트 디렉토리(폴더) 안에서 uv init</p>
<blockquote>
<pre><code>uv init</code></pre></blockquote>
<pre><code>
**2. 통합 requirements.txt인 clean_requirements.txt생성**

- 아래의 기능을 사용한 두개 경로 파일이 통합
..\requirements\fastapi-backend-requirements.txt
..\requirements\llm-requirements.txt

- 아래는 위 두 경로의 파일을 통합하여 clean_requirements.txt파일 생성 
&gt; Get-Content ..\requirements\fastapi-backend-requirements.txt, ..\requirements\llm-requirements.txt ` | Where-Object { $_ -match &#39;\S&#39; -and $_ -notmatch &#39;^\s*#&#39; } ` | ForEach-Object { ($_ -split &#39;#&#39;)[0].Trim() } ` | Where-Object { $_ } ` | ForEach-Object { $_ -replace &#39;\[.*?\]&#39;, &#39;&#39; } ` | Sort-Object | Get-Unique ` | Out-File clean_requirements.txt -Encoding utf8 

- 아래는 clean_requirements.txt의 형태
![](https://velog.velcdn.com/images/path_creator/post/a794a3d7-3749-48f9-a378-13df276272a9/image.png)

&lt;br/&gt;

**3. pyproject.toml 초반 세팅**
- 현재 아래는 설정을 맞추었지만 맨 아래 두개 섹션 추가 해야함 

&gt;```
[project]
name = &quot;the-project&quot;
version = &quot;0.1.0&quot;
description = &quot;Add your description here&quot;
readme = &quot;README.md&quot;
requires-python = &quot;&gt;=3.12&quot;
dependencies = []
&gt;
# 아래 부분을 추가
[dependency-groups]
dev = [
    &quot;pytest&gt;=8.2.0&quot;,
]
&gt;
# 이 부분을 추가하세요
[tool.setuptools.packages.find]
include = [&quot;app*&quot;]
exclude = [&quot;deploy*&quot;, &quot;tests*&quot;, &quot;*.egg-info&quot;, &quot;__pycache__&quot;]</code></pre><ul>
<li>디렉토리 안에 터미널에선 아래 두 섹션을 코드로 추가하기도 한다</li>
</ul>
<blockquote>
<p>uv add --dev &quot;pytest&gt;=8.2.0&quot;</p>
</blockquote>
<blockquote>
</blockquote>
<p>Add-Content pyproject.toml &quot;<code>n[tool.setuptools.packages.find]&quot;
Add-Content pyproject.toml &quot;include = [</code>&quot;app<em><code>&quot;]&quot;
Add-Content pyproject.toml &quot;exclude = [</code>&quot;deploy<em><code>&quot;,</code>&quot;tests</em><code>&quot;,</code>&quot;</em>.egg-info<code>&quot;,</code>&quot;<strong>pycache</strong>`&quot;]&quot;</p>
<br/>

<p><strong>4. 가상화 + 필요 라이브러리 모음 toml에 추가</strong></p>
<ul>
<li><p>가상화 환경 venv 생성</p>
<blockquote>
<p>uv venv</p>
</blockquote>
</li>
<li><p>가상화 활성화 (항상 켜놓고 작업 추천) </p>
<blockquote>
<p>.venv\Scripts\activate</p>
</blockquote>
</li>
<li><p>가상화 비활성화</p>
<blockquote>
<p>deactivate</p>
</blockquote>
</li>
<li><p>dependencies = [ ]의 안에 clean_requirements.txt 내용 추가
터미널에서 아래 코드를 치면 자동으로 clean_requirements.txt 내용물이 dependencies에 추가됨</p>
<blockquote>
</blockquote>
<p>uv add -r clean_requirements.txt</p>
</li>
<li><p>(선택) [dependency-groups]의 dev 섹션을 생성하여 clean_requirements.txt 내용 추가
터미널에서 아래 코드를 치면 자동으로 clean_requirements.txt 추가됨</p>
<blockquote>
<p>uv add --dev  -r clean_requirements.txt</p>
</blockquote>
</li>
<li><p>특정 패키지 업데이트가 필요할때는 아래처럼 하면된다. add는 추가/업데이트시 유용</p>
<blockquote>
<p>uv add --dev &quot;pytest&gt;=8.1.0&quot;</p>
</blockquote>
</li>
</ul>
<br/>

<p>*<em>5. 마무리 패키지 적용 + 운용
*</em></p>
<ul>
<li><p>uv run으로 현재 설치된 패키지 리스트 확인</p>
<blockquote>
<p>uv run</p>
</blockquote>
</li>
<li><p>uv sync만으로 프로젝트에 필요한 Python 버전과 .venv 가 자동으로 맞춰집니다.</p>
<blockquote>
<p>uv sync</p>
</blockquote>
</li>
<li><p>uv lock으로 <code>uv.lock</code> 파일을 생성해 의존성 버전을 고정합니다.
빠른 빌드와 재현성을 동시에 보장합니다.</p>
<blockquote>
<p>uv lock</p>
</blockquote>
</li>
</ul>
<h3 id="오류-상황">오류 상황</h3>
<ol>
<li><strong>가상 환경이 작동하지 않을 경우</strong></li>
</ol>
<ul>
<li><strong>가상 환경 폴더 삭제</strong></li>
</ul>
<blockquote>
<p>Remove-Item -Recurse -Force .venv</p>
</blockquote>
<ul>
<li><strong>venv라는 가상 환경 다시 생성</strong></li>
</ul>
<blockquote>
<p>uv venv</p>
</blockquote>
<ul>
<li><strong>가상화 활성화</strong></li>
</ul>
<blockquote>
<p>.venv\Scripts\activate</p>
</blockquote>
<ul>
<li><strong>동기화로 마무리</strong></li>
</ul>
<blockquote>
<p>UV SYNC</p>
</blockquote>
<h2 id="기타-추가-사항--디렉토리-추가">기타 추가 사항 : 디렉토리 추가</h2>
<ul>
<li>디렉토리 생성</li>
</ul>
<blockquote>
<p>New-Item -ItemType Directory -Force -Path app, <code>&quot;app\models&quot;,&quot;app\schemas&quot;,&quot;app\api&quot;,&quot;app\services&quot;,&quot;app\utils&quot;,&quot;app\tasks&quot;,&quot;app\tests&quot;,</code>
    &quot;alembic\versions&quot;,&quot;docker&quot;,&quot;scripts&quot;</p>
</blockquote>
<ul>
<li>py 코드 파일 생성 예시</li>
</ul>
<blockquote>
<p>New-Item -ItemType File -Force -Path `
    &quot;app_<em>init_</em>.py&quot;,
    &quot;app\main.py&quot;,
    &quot;app\config.py&quot;,
    &quot;app\database.py&quot;,
    &quot;app\models_<em>init_</em>.py&quot;,
    &quot;app\models\user.py&quot;,
    &quot;app\schemas_<em>init_</em>.py&quot;,
    &quot;app\schemas\user.py&quot;,
    &quot;app\api_<em>init_</em>.py&quot;,
    &quot;app\api\auth.py&quot;,
    &quot;app\api\websocket.py&quot;,
    &quot;app\services_<em>init_</em>.py&quot;,
    &quot;app\services\auth_service.py&quot;,
    &quot;app\services\redis_service.py&quot;,
    &quot;app\utils_<em>init_</em>.py&quot;,
    &quot;app\utils\security.py&quot;,
    &quot;app\utils\dependencies.py&quot;,
    &quot;app\utils\connection_manager.py&quot;,
    &quot;app\tasks_<em>init_</em>.py&quot;,
    &quot;app\tasks\tasks.py&quot;,
    &quot;app\tests_<em>init_</em>.py&quot;,
    &quot;app\tests\test_auth.py&quot;,
    &quot;app\tests\test_websocket.py&quot;,
    &quot;app\tests\test_profanity_filter.py&quot;,
    &quot;alembic\env.py&quot;,
    &quot;alembic\script.py.mako&quot;,
    &quot;docker\Dockerfile&quot;,
    &quot;docker\docker-compose.yml&quot;,
    &quot;docker\nginx.conf&quot;,
    &quot;scripts\init_db.py&quot;,
    &quot;scripts\run_migration.sh&quot;,
    &quot;.env.example&quot;,
    &quot;.gitignore&quot;,
    &quot;README.md&quot;</p>
</blockquote>
<p>참고: 
<a href="https://sigridjin.medium.com/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%9D%BC%EB%A9%B4-uv-%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%A9%EC%8B%9C%EB%8B%A4-546d523f7178">https://sigridjin.medium.com/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EA%B0%9C%EB%B0%9C%EC%9E%90%EB%9D%BC%EB%A9%B4-uv-%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%A9%EC%8B%9C%EB%8B%A4-546d523f7178</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[MCP와 Vibe Coding(바이브코딩) 추천 세팅 설정 ver.25년]]></title>
            <link>https://velog.io/@path_creator/MCP%EC%99%80-Vibe-Coding%EB%B0%94%EC%9D%B4%EB%B8%8C%EC%BD%94%EB%94%A9-%EC%B6%94%EC%B2%9C-%EC%84%B8%ED%8C%85-%EC%84%A4%EC%A0%95-ver.25%EB%85%84</link>
            <guid>https://velog.io/@path_creator/MCP%EC%99%80-Vibe-Coding%EB%B0%94%EC%9D%B4%EB%B8%8C%EC%BD%94%EB%94%A9-%EC%B6%94%EC%B2%9C-%EC%84%B8%ED%8C%85-%EC%84%A4%EC%A0%95-ver.25%EB%85%84</guid>
            <pubDate>Mon, 30 Jun 2025 16:42:04 GMT</pubDate>
            <description><![CDATA[<h4 id="해외-커뮤니티에선-윈드서프가-객체화나-디렉토리-전략에선-커서보다-더-낫다는-의견이-우세함----25년-5월--">해외 커뮤니티에선 윈드서프가 객체화나 디렉토리 전략에선 커서보다 더 낫다는 의견이 우세함  --25년 5월--</h4>
<h3 id="1-바이브코딩-도구-rules-설정">1. 바이브코딩 도구 Rules 설정</h3>
<h4 id="바이브코딩에서-rule의-개념">바이브코딩에서 Rule의 개념</h4>
<blockquote>
<p>바이브코딩에선 System prompt 개념과 동일한 rule이란 개념이 존재</p>
</blockquote>
<p>*<em>글로벌 규칙 *</em>(Global Rules) (global_rules.md): 모든 워크스페이스에 공통으로 적용됩니다.</p>
<p><strong>Workspace</strong> (Local Rules) (.windsurfrules): 특정 워크스페이스에만 적용됩니다.</p>
<h4 id="규칙-추가">규칙 추가</h4>
<p>*<em>채팅창 우측 상단 - customization 누르면 rules 세팅 열림 *</em>
<img src="https://velog.velcdn.com/images/path_creator/post/0115bcd5-a97f-49ce-bca5-bd3b7b9eb1be/image.png" alt="">global 규칙 추가는 코딩시에 항상 적용됨
<br/></p>
<p><strong>아래는 work space - 부분 규칙 추가</strong>
<img src="https://velog.velcdn.com/images/path_creator/post/29818ec5-f645-434b-9906-082c639d24b3/image.png" alt="">python.md 라는 이름으로 규칙 추가</p>
<br/>

<p><strong>아래는 work space - 부분 규칙 설정 전략</strong>
<img src="https://velog.velcdn.com/images/path_creator/post/022aad72-ea04-4946-b55b-ba9c8d893dc1/image.png" alt=""></p>
<p>*<em>Activation mode 종류 *</em> </p>
<ul>
<li>Manual -- &#39;@워크스페이스rule이름&#39;을 붙여서 활성화 가능</li>
<li>Always On -- 항상 활성화 가능</li>
<li>Model Decision -- 상황 설명을 추가해놓고, 그 상황에 직면 했을때 워크스페이스rule 활성화</li>
<li>Glob -- 특정 파일이나 디렉토리 구조에서 워크스페이스rule 활성화</li>
</ul>
<p>위 그림에선 <strong>glob</strong>로 선정했고 아래처럼 rule을 선정했다.
<strong>Activation mode</strong></p>
<blockquote>
<p>This rule applies to all *.py files.</p>
</blockquote>
<br/>
아래는 선정한 **Python Rules** (프롬프트 강화 적용)
띄워 쓰기를 구조적으로 하면 더 효과적으로 프롬프트 강화 가능

<blockquote>
</blockquote>
<pre><code>Target : when code in Python 3.10+ features (structural pattern matching, union operators, parenthesized context managers)
&gt;
- Implement proper generic types where applicable
&gt;
# Core Development Standards
## Error Handling &amp; Robustness
- Implement comprehensive exception handling with specific exception types
- Use context managers for resource management
- Validate input parameters and return meaningful error messages
- Log errors with appropriate severity levels using structured logging
&gt;
# Code Quality Guidelines
&gt;
## Documentation &amp; Comments
- Write comprehensive docstrings for all public functions and classes
- Add inline comments for complex algorithms and business logic
&gt;
## Code Style &amp; Formatting
- Follow PEP 8 style guidelines strictly
- Use consistent naming conventions (snake_case for functions/variables, PascalCase for classes)
&gt;
# Development Workflow
## Code Quality Verification
- Execute linting with ruff or pylint
- Format code with black
- Verify no security vulnerabilities with bandit
- Run static type checking with mypy
&gt;
## Testing Strategy (context-dependent)
### Baseline Requirements (All Projects)
- Validate edge cases and error conditions
- Test happy path functionality
&gt;
### Additional for Production
- Integration tests with mocked external services
- Performance benchmarks for critical paths
- Load testing for concurrent operations
&gt;
## Deployment Readiness
- Test pip package detection and installation
- Ensure both CLI and interactive modes function correctly
&gt;
# Development Tools (Optional)
Formatting: black(코드 포매터)
Linting: ruff(초고속 린터 코드 스타일 분석), pylint(종합 분석 도구)
Security: bandit(보안 취약점 스캐너), safety(설치 패키지 보안 위험 관리) 
Performance: py-spy(프로세스 성능 분석), memory-profiler(메모리 사용량 분석)
Test: pytest, coverage
Dependency: Poetry
&gt;
## Production Services
- Apply all guidelines strictly
- Full documentation and performance optimization
- Implement monitoring and logging strategies
&gt;
## Data Science Projects
- Emphasize: Data validation, reproducible results
- Use appropriate libraries (pandas, numpy, scikit-learn)
- Document data processing steps and assumptions
- Handle missing values and edge cases gracefully</code></pre><p>프롬프트는 반복되는 말이 많으면 혼란을 가중 시키므로 그 부분을 조심히 한다.
<br/>
<strong>Global Rule (User Rule)</strong> (프롬프트 강화 적용)
개발에 항상 적용되는 rule 프롬프트</p>
<blockquote>
</blockquote>
<pre><code># Role
Professional software engineer developing production-ready services with clean architecture, applying SOLID principles and some GoF Design Patterns, and functional programming concepts optimized for automated development environments.
&gt;
1. Core Development Standards
## Error Handling &amp; Robustness
- Implement exception handling with specific exception types
- Validate input parameters 
- Log errors with appropriate severity levels using structured logging
## Performance &amp; Optimization
- Use context7 mcp
- Profile code performance for critical paths
- Use appropriate data structures and algorithms
- Optimize for memory efficiency when handling large datasets
- Avoid over-engineering: do not introduce unnecessary abstraction and complexity
## Security Considerations
- Use environment variables for sensitive configuration
- Implement proper authentication and authorization patterns
- Avoid hardcoded secrets and credentials
&gt;
2. Code Quality Guidelines
## Structure &amp; Maintainability
- Single Responsibility: One function, one clear purpose
- Keep functions under 20 lines when possible
- Use descriptive variable and function names (no abbreviations)
- Organize code blocks logically
- Organize imports/includes following language conventions
- Group related functionality into cohesive modules/packages
- Apply appropriate design patterns contextually
## Documentation &amp; Comments
- Include parameter types, return types, and example usage in comments
- Add clear comments for complex logic and business decisions
- Document design decisions and architectural choices
## Code Style &amp; Formatting
- Use consistent naming conventions
- Maintain line length under 88 characters
&gt;
3. AI-Assisted Development Guidelines
- Provide clear, actionable code suggestions with rationale
- Include alternative implementation approaches when relevant
- Explain trade-offs between different solutions
&gt;
4. Development Workflow
## Code Quality Verification
- Perform static type checking where applicable
## Testing Strategy (context-dependent, optional)
- Write unit tests for all core business logic
- Include integration tests for external dependencies
## Deployment Readiness
- Verify all dependencies are properly declared
- Ensure proper configuration management
- Validate environment setup
&gt;
5. Context-Specific Applications
## Simple Scripts &amp; Prototypes
- Prioritize: Type hints, error handling, clean structure
- Focus: Quick implementation with maintainable code</code></pre><br/>

<h4 id="추가-정보-workflows">추가 정보 Workflows</h4>
<p>rules 선택창 옆에** Workflows가** 존재
<strong>Workflows는</strong> 특정 명령어 설정으로 <strong>작업 순서 flows를</strong> 정하고 자동으로 작업 진행하는 기능(자유도에 약간의 제한 존재)</p>
<p>참조 링크:
<a href="https://www.paulmduvall.com/using-windsurf-rules-workflows-and-memories/">https://www.paulmduvall.com/using-windsurf-rules-workflows-and-memories/</a></p>
<h3 id="2-바이브코딩-mcp-설정">2. 바이브코딩 MCP 설정</h3>
<p><strong>LLM의 이해도를 위하여</strong> 수정하려는 코드와 Rule을 언급하며 코드 수정
<img src="https://velog.velcdn.com/images/path_creator/post/d577e952-5317-4708-9c57-37f716f4014c/image.png" alt="">
채팅에서 &quot;@&quot;를 쳐서 해당 코드 이름을 언급하던가, &quot;+&quot;를 클릭하여 필요 파일 선택 
<br/>
<strong>클로드코드</strong>는 함수나 클래스 하나 단위에서 <strong>문제 해결력 최고</strong> -25년5월-</p>
<p>아래 링크는 바이브코딩 도구와 클로드코드를 함께 사용하는 방법이 기재 되있음.
<a href="https://velog.io/@path_creator/zed-ai-%EC%9C%88%EB%8F%84%EC%9A%B0-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%84%B8%ED%8C%85">https://velog.io/@path_creator/zed-ai-%EC%9C%88%EB%8F%84%EC%9A%B0-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%84%B8%ED%8C%85</a></p>
<h4 id="windsurf-핵심-기능">Windsurf 핵심 기능</h4>
<ul>
<li><strong>SWE-1 모델 사용 : *<em>작업 플로우나 프로젝트 구조 형성에 특화된 윈드서프 전용 모델. 따라서 초반에 프로젝트 구성이나 배포 형태는 *</em>SWE-1</strong> 모델을 사용</li>
</ul>
<p>참조: <a href="https://apidog.com/kr/blog/windsurf-swe-1-kr/">https://apidog.com/kr/blog/windsurf-swe-1-kr/</a></p>
<ul>
<li>*<em>cascade 기능 : *</em>작업들의 메모리를 관리하고 인지하는 기능 -현재 Cursor도 이와 유사한 기능 존재
<img src="https://velog.velcdn.com/images/path_creator/post/c7e4e541-51fd-4a3b-be47-7560647e07dc/image.png" alt=""></li>
</ul>
<ul>
<li><p>*<em>browser 분석 기능 : *</em>해당 브라우저 분석 기능을 클릭하고 해당 브라우저의 프론트앤드를 복사 활용 가능</p>
</li>
<li><p>*<em>plan mode 기능 : *</em>현재 구성하려는 프로젝트의 설명을 듣고 이에 맞게 프로젝트의 작업 순서를 구성하여 단기, 시스템 프롬프트를 구성해준다.
<img src="https://velog.velcdn.com/images/path_creator/post/76590306-95ba-4ac2-bb14-984daf651d3d/image.png" alt=""></p>
</li>
</ul>
<p>ai 채팅창에서 우측 하단의 버튼을 누르고 이에 맞는 프롬프트를 넣으면 됨.</p>
<p>*<em>! 주의 : *</em>mcp 적용하기 이전에 plan mode가 발동되기 때문에 mcp 적용 이후 새채팅에서 플랜을 짜는식으로 전략을 쓰는 것이 유용</p>
<br/>

<h4 id="windsurf-mcp-설치법">Windsurf mcp 설치법</h4>
<p>*<em>file - &gt; preference -&gt; windsurf setting -&gt; mcp라 검색 *</em>
<img src="https://velog.velcdn.com/images/path_creator/post/6b2b0306-b466-476a-bdf0-dd952ac48499/image.png" alt=""></p>
<p>open plugin store 메뉴 선택 -&gt; 필요한 mcp 검색</p>
<br/>

<p><strong>여러 외부 mcp 설치법 리스트 모음</strong>
<a href="https://himcp.ai/">https://himcp.ai/</a> -- 분야별 MCP 정보 모음</p>
<p><a href="https://smithery.ai/">https://smithery.ai/</a> -- 각 ai의 MCP 정보들과 설치법 + 웹에서 MCP를 테스트 할 수 있는 도구</p>
<p><a href="https://github.com/modelcontextprotocol/servers/tree/main?tab=readme-ov-file">https://github.com/modelcontextprotocol/servers/tree/main?tab=readme-ov-file</a>  -- 각 ai의 MCP 설치법 링크</p>
<p><strong>MCP 설치 방법 3가지</strong></p>
<ul>
<li>로컬 설치</li>
<li>npx(속도 더빠름)나 pip 으로 터미널에서 설치 (선호 가장 간단)</li>
<li>smithery 같은 서버에서 설치하고 관리 접속 (불안정한 편)</li>
<li>mcp manage plugin에 필요한 mcp를 mcp_config.json에 복사 붙여 넣기</li>
</ul>
<p><strong>mcp manage plugin json 파일 예제</strong></p>
<blockquote>
</blockquote>
<pre><code>{
  &quot;mcpServers&quot;: {
  &gt;
    &quot;sequential-thinking&quot;: {
      &quot;command&quot;: &quot;npx&quot;,
      &quot;args&quot;: [
        &quot;-y&quot;,
        &quot;@modelcontextprotocol/server-sequential-thinking&quot;
      ],
      &quot;env&quot;: {}
    },
    &gt;
    &quot;context7&quot;: {
      &quot;command&quot;: &quot;npx&quot;,
      &quot;args&quot;: [
        &quot;-y&quot;,
        &quot;@upstash/context7-mcp@latest&quot;
      ],
      &quot;disabledTools&quot;: []
    },
    &gt;
    &quot;mcp-supermemory-ai&quot;: {
      &quot;command&quot;: &quot;npx&quot;,
      &quot;args&quot;: [
        &quot;-y&quot;,
        &quot;supergateway&quot;,
        &quot;--sse&quot;,
&gt;        &quot;https://mcp.supermemory.ai/4dfYOz_KTNMRWCqpcCLXq/sse&quot;
      ]
    }
&gt;    
}</code></pre><p><strong>추천 MCP 설치법</strong></p>
<p><strong>Super Memory</strong>
메모리를 체계적으로 저장하는 mcp로 외부에서 따로 설치
<img src="https://velog.velcdn.com/images/path_creator/post/7d1a261b-5af4-47b4-9574-5e12c8493420/image.png" alt="">
링크 : <a href="https://mcp.supermemory.ai/">https://mcp.supermemory.ai/</a></p>
<p><strong>DuckDuckGo Search Server</strong>
옛날 정보가 아닌 최근 정보 위주로 인터넷에 접근하여 결과를 보여주는mcp</p>
<p><a href="https://smithery.ai/server/@nickclyde/duckduckgo-mcp-server">https://smithery.ai/server/@nickclyde/duckduckgo-mcp-server</a>
사이트에 접속하여, 해당 ai 도구에 맞게 설치</p>
<blockquote>
<p>npx -y @smithery/cli@latest install @nickclyde/duckduckgo-mcp-server --client windsurf --key 8bee2c34-a3eb-4cfc-b687-455c6ec5c6ff</p>
</blockquote>
<p>위 명령어 터미널이나 power shell에서 설치</p>
<p><strong>sequential-thinking</strong>
프롬프트를 읽고 단계적으로 해석하거나 cot화 시키면서 프롬프트 강화하는 mcp -- 사용시 시간이 오래걸리는 단점 발생
아래처럼 mcp_config.json에 sequential-thinking을 추가하여 정착</p>
<blockquote>
<pre><code>{
  &quot;mcpServers&quot;: {

    &quot;sequential-thinking&quot;: {
      &quot;command&quot;: &quot;npx&quot;,
      &quot;args&quot;: [&quot;-y&quot;, &quot;@modelcontextprotocol/server-sequential-thinking&quot;],
      &quot;env&quot;: {}
    }
}</code></pre></blockquote>
<h4 id="extension-설치-추천">Extension 설치 추천</h4>
<p><strong>Markdown Preview Mermaid Support</strong>
다이어그램이나 코드의 클래스 구조, 프로젝트의 도식화 등을 markdown 언어로 구성가능</p>
<p>참고 : <a href="https://velog.io/@brown_eyed87/221006Mermaid%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-Markdown-%EC%9E%91%EC%84%B1">https://velog.io/@brown_eyed87/221006Mermaid%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%9C-Markdown-%EC%9E%91%EC%84%B1</a></p>
<p><strong>Shortcut menu bar</strong></p>
<p><a href="https://marketplace.visualstudio.com/items?itemName=jerrygoyal.shortcut-menu-bar">https://marketplace.visualstudio.com/items?itemName=jerrygoyal.shortcut-menu-bar</a></p>
<p><strong>indent-rainbow</strong>
코드의 들여쓰기 띄워쓰기를 색깔로 표시해주는 띄워쓰기 표시 도구</p>
<p><strong>Code Spell Checker</strong>
코드의 잘못된 스펠링 밑줄 쳐줌</p>
<p><strong>Bookmarks</strong>
북마크 가능한 익스텐션 </p>
<ul>
<li>마우스 우클릭 후 add bookmark로 코드 라인 북마크</li>
<li><em>Ruff*</em>
파이썬에 특화된 포맷터 ctrl+shift+p로 선택한 코드 자동 포맷 정리 </li>
<li>Shift+alt+F로 선택된 코드 블록 자동 포맷화</li>
</ul>
<p><strong>Prettier</strong>
다른 일반적인 언어의 포맷터</p>
<p><strong>Surround</strong>
제어문이나 조건문을 자동 생성</p>
<ul>
<li>변수 마우스로 선택 후, Ctrl+Shift+t 누르면 제어문 선택 리스트 노출</li>
</ul>
<p><strong>Bracket Pair Colorizer 2</strong>
코드의 괄호를 등차별로 다양한 색깔을 입혀줌</p>
<p><strong>Output Colorizer</strong>
로그에 색깔을 입혀 가독성에 특화함</p>
<p><strong>Better Colorizer</strong>
json이나 git등에 색깔을 입혀 가독성에 특화함</p>
<p><strong>DotENV</strong>
env파일에 색깔을 입혀 가독성에 특화함</p>
<p><strong>colorize</strong>
css나 프론트앤드에서 색깔 코드 자동으로 색깔로 변환된 하일라이트 쳐줌</p>
<p><strong>Error Lens</strong>
실시간 오류를 하이라이트로 표시함</p>
<p><strong>GitLens</strong>
VSCode 내에서 GIT 버젼 관리와 코딩을 동시에 수행하는데 가능</p>
<p><strong>git graph</strong>
git hub 작업을 flow 그래프 형태로 협업 확인 가능 </p>
<h3 id="바이브코딩mcp-활용">바이브코딩+MCP 활용</h3>
<p>*<em>!!주의 : *</em>mcp는 chat 창에서 &quot;@&quot;를 붙이고 mcp 명칭을 불러 활용하는 경우가 많다. 자동으로 mcp가 발동되는 경우도 있지만 안그런경우도 많다.</p>
<h4 id="단계-1-프로젝트-기획-및-시장-조사">단계 1: 프로젝트 기획 및 시장 조사</h4>
<p><strong>목표: *<em>트렌드 파악 및 기술 환경 분석
*</em>DuckDuckGo MCP 활용</strong> 
프롬프트 예:</p>
<blockquote>
<p>AI 백엔드 서비스에서 가장 많이 사용되는 Python 프레임워크 디렉토리 구조 찾아줘 USE DuckDuckGo MCP</p>
</blockquote>
<h4 id="단계-2-기술-스택-선택-및-아키텍처-설계">단계 2: 기술 스택 선택 및 아키텍처 설계</h4>
<p><strong>Context7 + Sequential Thinking 조합 활용</strong>
프롬프트 예:</p>
<blockquote>
<p>FastAPI의 최신 비동기 처리 방법과 WebSocket 구현 방법 알려줘. use context7</p>
</blockquote>
<blockquote>
<p>SQLAlchemy 2.0의 새로운 ORM 패턴과 성능 최적화 방법 설명해줘. use context7</p>
</blockquote>
<p><strong>Sequential Thinking</strong></p>
<blockquote>
<p>위에서 조사한 기술들을 바탕으로 실시간 데이터 분석 백엔드 아키텍처를 단계별로 설계해줘. Use Sequential Thinking
고려사항:</p>
</blockquote>
<ul>
<li>예상 동시 사용자: 10,000명</li>
<li>실시간 데이터 처리량: 초당 1,000건</li>
<li>데이터 지연시간: 100ms 이하</li>
<li>확장성과 유지보수성 중시</li>
</ul>
<p><strong>Supermemory에 설계 결정 저장</strong></p>
<blockquote>
<p>방금 설계한 아키텍처의 핵심 결정사항들을 내 프로젝트 메모리에 저장해줘:</p>
</blockquote>
<ul>
<li>선택한 기술 스택과 이유</li>
<li>성능 요구사항</li>
<li>아키텍처 패턴 결정 근거</li>
</ul>
<h4 id="단계-3-개발-환경-설정-및-초기-구현">단계 3: 개발 환경 설정 및 초기 구현</h4>
<blockquote>
<p>&quot;FastAPI + SQLAlchemy + Redis + Docker를 사용한 최신 프로젝트 구조와 설정 방법 알려줘. use context7&quot;</p>
</blockquote>
<p>&quot;Poetry를 사용한 Python 의존성 관리와 가상환경 설정 최신 방법 보여줘. use context7&quot;</p>
<blockquote>
</blockquote>
<p>&quot;Docker Compose를 사용한 개발 환경 구성 베스트 프랙티스 알려줘. use context7&quot;</p>
<h4 id="단계-4-문제-해결-및-성능-최적화">단계 4: 문제 해결 및 성능 최적화</h4>
<blockquote>
<p>&quot;FastAPI의 dependency injection을 사용한 데이터베이스 연결 풀 최적화 방법 알려줘. use context7&quot;</p>
</blockquote>
<blockquote>
<p>&quot;FastAPI의 ** 서비스 코드 만들어줘. use context7&quot;</p>
</blockquote>
<h4 id="memory-bank-rule-활용-자동-프로젝트-업데이트와-메모리-기록">Memory bank rule 활용 (자동 프로젝트 업데이트와 메모리 기록)</h4>
<p>*<em>사용 이점 : *</em> </p>
<ul>
<li>프로젝트 맥락 플로우 유지에 효과적(정교한 프로젝트 구성 서포트)</li>
<li>프로젝트 구조 업데이트시 자동 기록(프로젝트 진행 현황 추적 기능)</li>
<li>LLM의 인식 구조 개선과 더 빠른 코드 생성</li>
<li>목적에 맞춘 개발 전략 자동 업데이트</li>
</ul>
<p>활용하기에 앞서 <strong>auto-generate-memories를 끈다</strong></p>
<p><img src="https://velog.velcdn.com/images/path_creator/post/8b686711-8039-471c-b3cc-4edd468751e5/image.png" alt=""></p>
<p>이후 Workspace에서** memory-bank.md 생성**
*<em>경로 예 : *</em>C:\Users\USER.....windsurf\rules에서 생성</p>
<p>아래처럼 5가지 
  decisionLog.md:
    트리거: &quot;중대한 아키텍처 결정이 내려질 때 (새 구성요소, 데이터 흐름 변경, 기술 선택 등).&quot;
  productContext.md:
    트리거: &quot;고수준 프로젝트 설명, 목표, 기능 또는 전체 아키텍처가 크게 변경될 때.&quot;
  systemPatterns.md:
    트리거: &quot;새로운 아키텍처 패턴이 도입되거나 기존 패턴이 수정될 때.&quot;
  activeContext.md:
    트리거: &quot;현재 작업 초점이 변경되거나 중요한 진전이 있을 때.&quot;
  progress.md:
    트리거: &quot;작업이 시작되거나 완료되거나 상태가 변경될 때.&quot;</p>
<p><strong>memory-bank.md 프롬프트</strong></p>
<blockquote>
</blockquote>
<p>memory_system_rules:
  primary_system: &quot;memory-bank&quot;</p>
<blockquote>
<p>initialization:
  trigger: &quot;first_interaction&quot;
  priority: &quot;immediate&quot;
  required: true
  actions:
    - &quot;Before doing ANYTHING else, read and fully internalize ALL rules in this file.&quot;
    - &quot;Check if memory-bank/ directory exists.&quot;
    - &quot;If memory-bank exists: Read all core files (productContext.md, activeContext.md, systemPatterns.md, decisionLog.md, progress.md). Set status to [MEMORY BANK: ACTIVE].&quot;
    - &quot;If memory-bank does NOT exist: Inform user. Ask to create and provide yes and no response choices. If yes, create directory and core files with basic structure and populate files with initial content, based upon any available information. If no, set status to [MEMORY BANK: INACTIVE].&quot;
    - &quot;Load context from memory-bank files if active.&quot;
    - &quot;Proceed with task or if no task is given, suggest 2-4 tasks based upon memory-bank/ content.&quot;</p>
</blockquote>
<p>  validation:
    - &quot;Verify memory-bank status (ACTIVE/INACTIVE).&quot;
    - &quot;If ACTIVE, confirm core files were read.&quot;</p>
<blockquote>
</blockquote>
<p>system_validation:
  startup:
    - &quot;Verify .windsurfrules loaded&quot;
    - &quot;Check memory-bank accessibility if expected&quot;
    - &quot;Confirm initialization sequence complete&quot;</p>
<blockquote>
</blockquote>
<p>memory_bank:
  core_files:
    activeContext.md:
      purpose: &quot;Track session state and goals (objectives, decisions, questions, blockers)&quot;
    productContext.md:
      purpose: &quot;Define project scope (overview, components, organization, standards)&quot;
    progress.md:
      purpose: &quot;Track work status (completed, current, next, issues)&quot;
    decisionLog.md:
      purpose: &quot;Record decisions (technical, architecture, implementation, alternatives)&quot;
    systemPatterns.md: # Optional but recommended
      purpose: &quot;Document recurring patterns and standards (coding, architecture, testing)&quot;
  file_handling:
    read_all_at_startup: true # Implied by initialization actions
    build_complete_context: true # Implied by initialization actions</p>
<blockquote>
</blockquote>
<p>general:
  status_prefix: &quot;Begin EVERY response with either &#39;[MEMORY BANK: ACTIVE]&#39; or &#39;[MEMORY BANK: INACTIVE]&#39;, according to the current state of the Memory Bank.&quot;</p>
<blockquote>
</blockquote>
<p>memory_bank_updates:
  frequency: &quot;UPDATE MEMORY BANK THROUGHOUT THE CHAT SESSION, WHEN SIGNIFICANT CHANGES OCCUR IN THE PROJECT. Use judgment to determine significance.&quot;
  decisionLog.md:
    trigger: &quot;When a significant architectural decision is made (new component, data flow change, technology choice, etc.).&quot;
    action: &quot;Append new information (decision, rationale, implications) using insert_content. Never overwrite. Include timestamp.&quot;
    format: &quot;[YYYY-MM-DD HH:MM:SS] - [Summary of Decision]&quot;
  productContext.md:
    trigger: &quot;When the high-level project description, goals, features, or overall architecture changes significantly.&quot;
    action: &quot;Append new information or modify existing entries using insert_content or apply_diff. Append timestamp and summary as footnote.&quot;
    format: &quot;[YYYY-MM-DD HH:MM:SS] - [Summary of Change]&quot;
  systemPatterns.md:
    trigger: &quot;When new architectural patterns are introduced or existing ones are modified.&quot;
    action: &quot;Append new patterns or modify existing entries using insert_content or apply_diff. Include timestamp.&quot;
    format: &quot;[YYYY-MM-DD HH:MM:SS] - [Description of Pattern/Change]&quot;
  activeContext.md:
    trigger: &quot;When the current focus of work changes, or when significant progress is made.&quot;
    action: &quot;Append to the relevant section (Current Focus, Recent Changes, Open Questions/Issues) or modify existing entries using insert_content or apply_diff. Include timestamp.&quot;
    format: &quot;[YYYY-MM-DD HH:MM:SS] - [Summary of Change/Focus/Issue]&quot;
  progress.md:
    trigger: &quot;When a task begins, is completed, or its status changes.&quot;
    action: &quot;Append the new entry using insert_content. Never overwrite. Include timestamp.&quot;
    format: &quot;[YYYY-MM-DD HH:MM:SS] - [Summary of Progress Update]&quot;</p>
<blockquote>
</blockquote>
<p>umb: # Update Memory Bank command
  trigger: &quot;^(Update Memory Bank|UMB)$&quot;
  instructions:
    - &quot;Halt Current Task: Stop current activity.&quot;
    - &quot;Acknowledge Command: Respond with &#39;[MEMORY BANK: UPDATING]&#39;.&quot;
    - &quot;Review Chat History: Analyze the complete current chat session.&quot;
  core_update_process: |
      1. Current Session Review: Analyze chat history for relevant decisions, context changes, progress updates, clarifications etc.
      2. Comprehensive Updates: Update relevant memory bank files based on the review, following the rules defined in &#39;memory_bank_updates&#39;.
      3. Memory Bank Synchronization: Ensure consistency across updated files.</p>
<blockquote>
</blockquote>
<p>  task_focus: &quot;During UMB, focus ONLY on capturing information explicitly present in the <em>current chat session</em> (clarifications, decisions, progress). Do NOT summarize the entire project or perform actions outside this scope.&quot;
  cross_mode_updates: &quot;Capture relevant information from the chat session irrespective of conceptual &#39;modes&#39; mentioned, adding it to the appropriate Memory Bank files.&quot;</p>
<blockquote>
<p>  post_umb_actions:
    - &quot;State: Memory Bank fully synchronized based on current chat session.&quot;
    - &quot;State: Session context preserved for continuation.&quot;</p>
</blockquote>
<p>*<em>원리 : *</em>위 프롬프트는 여러 목적에 따른 프롬프트들을 생성하고 작업 변경사항이 생기면 자동으로 프롬프트들이 변형된다. </p>
<p>출처 : <a href="https://github.com/GreatScottyMac/cascade-memory-bank?tab=readme-ov-file">https://github.com/GreatScottyMac/cascade-memory-bank?tab=readme-ov-file</a></p>
<h4 id="바이브코딩시에-memorial-bank-사용법">바이브코딩시에 Memorial bank 사용법</h4>
<ol>
<li>빈 디렉토리 생성</li>
<li>만든 디렉토리에 memory-bank 디렉토리 생성
progress, activeContext, systemPatterns, productContext, decisionLog 등의 목적으로 작업 기록과 개발 방법론 패턴 적용</li>
<li>아래처럼 first_interaction 치고 요구사항 치기<blockquote>
<p>예시 최초의 프롬프트
first_interaction
python 3.10+
fastapi    Pydantic v2 기반
uvicorn    0.29.0<br>python-dotenv    1.0.1    .env 기반 설정 관리
httpx    0.27.0    외부 API 호출용
pydantic    2.7.1    FastAPI 기본 모델
sqlalchemy    2.0.30    ORM
alembic    1.13.1    DB 스키마 관리
asyncpg    0.29.0    PostgreSQL 전용이며 도커에서 활용</p>
</blockquote>
</li>
</ol>
<p>위와 같은 프롬프트를 최조로 생성한다면 자동으로 메모리 관리 프롬프트 rule과 개발 관련 방법론 프롬프트 rule이 생성하여 목적에 따른 작업 기록틀을 memory-bank에 생성</p>
<p>아래처럼 해당 프로젝트의 memory-bank에서 새로운 rule들+기록 일지가 .md로 생성됨
<img src="https://velog.velcdn.com/images/path_creator/post/b56c0742-c15f-40ce-b0e3-0e655272934f/image.png" alt=""></p>
<p>앞으로 작업이 변경이 많아지거나 할때마다. 몇몇 기록이 자동 업데이트됨</p>
<p>*<em>추가 : *</em>현재 memomry-bank.md는 더 강화가 가능한 rule로 개선이 가능하며 여러 목적을 가진 rule을 생성되도록 하는게 건강한 바이브코딩 설계 방향이다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[fast api+python 유용한 보조 라이브러리]]></title>
            <link>https://velog.io/@path_creator/fast-apipython-%EC%9C%A0%EC%9A%A9%ED%95%9C-%EB%B3%B4%EC%A1%B0-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC</link>
            <guid>https://velog.io/@path_creator/fast-apipython-%EC%9C%A0%EC%9A%A9%ED%95%9C-%EB%B3%B4%EC%A1%B0-%EB%9D%BC%EC%9D%B4%EB%B8%8C%EB%9F%AC%EB%A6%AC</guid>
            <pubDate>Thu, 12 Jun 2025 07:38:56 GMT</pubDate>
            <description><![CDATA[<h4 id="fastapi파이썬에서-사용되면-좋은-라이브러리들을-소개해보겠다">Fastapi+파이썬에서 사용되면 좋은 라이브러리들을 소개해보겠다.</h4>
<h4 id="1코드-포매팅-2코드-분석-3보안-4성능-분석-5테스팅-6의존성-관리">1.코드 포매팅, 2.코드 분석, 3.보안, 4.성능 분석, 5.테스팅, 6.의존성 관리</h4>
<h2 id="1코드-포매팅">1.코드 포매팅</h2>
<h3 id="pip-install-black">pip install black</h3>
<p><strong>목적 :</strong>  &quot;하나의 명확한 방법&quot;으로 코드 포맷을 표준화
<strong>사용 용도 :</strong> 파이썬 코드를 일관된 스타일로 자동 포매팅
<strong>사용시 장점 :</strong></p>
<ul>
<li>코드 스타일 일관성 보장</li>
<li>코드 리뷰 시 스타일 관련 논의 제거</li>
<li>개발자 간 스타일 통일</li>
</ul>
<p><strong>사용 예시 :</strong></p>
<blockquote>
<pre><code class="language-python">&quot;&quot;&quot; 파이썬 해당 파일만 포매팅 할때 &quot;&quot;&quot; 
black my_script.py
&quot;&quot;&quot; 디렉토리 전체 포매팅 할때&quot;&quot;&quot; 
black src/
&quot;&quot;&quot;   설정 파일 (pyproject.toml) &quot;&quot;&quot; 
[tool.black]
line-length = 88
target-version = [&#39;py39&#39;]</code></pre>
</blockquote>
<p><strong>출력 예시 :</strong></p>
<blockquote>
<p><strong>적용 전</strong></p>
</blockquote>
<pre><code class="language-python">user_info={&#39;name&#39;:&#39;김철수&#39;,&#39;age&#39;:25,&#39;skills&#39;:[&#39;python&#39;,&#39;django&#39;,&#39;react&#39;]}
filtered_skills=[skill.upper() for skill in user_info[&#39;skills&#39;] if len(skill)&gt;5]
print(f&quot;User: {user_info[&#39;name&#39;]}, Skills: {&#39;, &#39;.join(filtered_skills)}&quot;)</code></pre>
<blockquote>
<p><strong>적용 후</strong></p>
</blockquote>
<pre><code class="language-python">user_info = {&quot;name&quot;: &quot;김철수&quot;, &quot;age&quot;: 25, &quot;skills&quot;: [&quot;python&quot;, &quot;django&quot;, &quot;react&quot;]}
filtered_skills = [
    skill.upper() for skill in user_info[&quot;skills&quot;] if len(skill) &gt; 5
]
print(f&quot;User: {user_info[&#39;name&#39;]}, Skills: {&#39;, &#39;.join(filtered_skills)}&quot;)</code></pre>
<h2 id="2코드-분석">2.코드 분석</h2>
<h3 id="pip-install-ruff">pip install ruff</h3>
<p><strong>목적 :</strong> 모든 코드 품질 검사를 하나의 빠른 도구로 통합
<strong>사용 용도 :</strong> 파이썬 코드의 스타일, 버그, 복잡성을 초고속으로 분석
<strong>사용시 장점 :</strong> </p>
<ul>
<li>기존 도구 대비 10-100배 빠른 속도</li>
<li>flake8, pylint, isort 등 여러 도구 기능 통합</li>
<li>자동 수정 기능 제공</li>
</ul>
<p><strong>사용 예시 :</strong></p>
<blockquote>
</blockquote>
<pre><code class="language-python">&quot;&quot;&quot; 코드 검사 &quot;&quot;&quot;
ruff check .
&quot;&quot;&quot; 자동 수정 &quot;&quot;&quot;
ruff check --fix .
&quot;&quot;&quot; 설정 파일 (pyproject.toml) &quot;&quot;&quot;
[tool.ruff]
line-length = 88
select = [&quot;E&quot;, &quot;F&quot;, &quot;W&quot;, &quot;B&quot;, &quot;I&quot;]</code></pre>
<p><strong>출력 예시 :</strong></p>
<blockquote>
<p>src/main.py:12:1: F401 [<em>] <code>sys</code> imported but unused
src/main.py:23:80: E501 Line too long (95 &gt; 88 characters)
src/main.py:45:1: I001 [</em>] Import block is un-sorted or un-formatted
Found 3 errors (2 fixable with <code>--fix</code>)</p>
</blockquote>
<h3 id="pip-install-pylint">pip install pylint</h3>
<p><strong>목적 :</strong> 코드 품질의 종합적 평가 및 개선점 제시
<strong>사용 용도 :</strong> 코드 품질, 스타일, 잠재적 버그를 종합적으로 분석
<strong>사용시 장점 :</strong></p>
<ul>
<li>매우 상세한 코드 분석</li>
<li>코드 점수 및 등급 제공</li>
<li>커스텀 체커 개발 가능</li>
</ul>
<p><strong>사용 예시 :</strong></p>
<blockquote>
<pre><code>&quot;&quot;&quot; 파일 분석 &quot;&quot;&quot;
pylint my_script.py
&quot;&quot;&quot; 설정 파일 생성 &quot;&quot;&quot;
pylint --generate-rcfile &gt; .pylintrc</code></pre></blockquote>
<pre><code>
**출력 예시 :**
&gt;```
************* Module main
main.py:1:0: C0114: Missing module docstring (missing-module-docstring)
main.py:5:0: C0116: Missing function or method docstring (missing-function-docstring)
main.py:12:4: W0613: Unused argument &#39;args&#39; (unused-argument)
Your code has been rated at 7.50/10</code></pre><h2 id="3보안">3.보안</h2>
<h3 id="pip-install-bandit">pip install bandit</h3>
<p><strong>목적 :</strong> 코드 레벨에서의 보안 위험 사전 제거
<strong>사용 용도 :</strong> 파이썬 코드에서 보안 취약점을 자동으로 탐지
<strong>사용시 장점 :</strong></p>
<ul>
<li>일반적인 보안 취약점 자동 탐지</li>
<li>OWASP Top 10 기반 검사</li>
<li>CI/CD 파이프라인 통합 가능</li>
</ul>
<p><strong>사용 예시 :</strong></p>
<blockquote>
</blockquote>
<pre><code class="language-python"># 보안 스캔
bandit -r src/
# JSON 리포트 생성
bandit -r src/ -f json -o security_report.json</code></pre>
<p><strong>출력 예시 :</strong></p>
<blockquote>
</blockquote>
<pre><code>&gt;&gt; Issue: [B105:hardcoded_password_string] Possible hardcoded password: &#39;admin123&#39;
   Severity: Medium   Confidence: Medium
   Location: ./src/config.py:15</code></pre><h3 id="pip-install-safety">pip install safety</h3>
<p><strong>목적 :</strong> 서드파티 패키지의 보안 위험 관리
<strong>사용 용도 :</strong> 설치된 패키지들의 알려진 보안 취약점 검사
<strong>사용시 장점 :</strong></p>
<ul>
<li>실시간 보안 데이터베이스 연동</li>
<li>requirements.txt 기반 자동 검사</li>
<li>취약점 심각도별 분류</li>
</ul>
<p><strong>사용 예시 :</strong></p>
<blockquote>
</blockquote>
<pre><code># 현재 환경 검사
safety check
# requirements.txt 검사
safety check -r requirements.txt</code></pre><p><strong>출력 예시 :</strong></p>
<blockquote>
</blockquote>
<pre><code>╒══════════════════════════════════════════════════════════════════════════════╕
│ found 2 known security vulnerabilities in 156 packages                      │
╘══════════════════════════════════════════════════════════════════════════════╛
╒══════════════════════════════════════════════════════════════════════════════╕
│ VULNERABILITY ID: 42194                                                     │
│ PACKAGE NAME: urllib3                                                       │
│ INSTALLED VERSION: 1.25.6                                                   │
│ AFFECTED VERSIONS: &lt;1.26.5                                                  │
│ VULNERABILITY: CVE-2021-33503                                               │
╘══════════════════════════════════════════════════════════════════════════════╛</code></pre><h2 id="4성능-분석">4.성능 분석</h2>
<h3 id="pip-install-py-spy">pip install py-spy</h3>
<p><strong>목적 :</strong> 성능 병목 지점의 정확한 식별
<strong>사용 용도 :</strong> 실행 중인 파이썬 프로세스의 성능을 실시간으로 프로파일링
<strong>사용시 장점 :</strong></p>
<ul>
<li>코드 수정 없이 프로파일링 가능</li>
<li>프로덕션 환경에서도 안전하게 사용</li>
<li>플레임 그래프 시각화 제공</li>
</ul>
<p><strong>사용 예시 :</strong></p>
<blockquote>
</blockquote>
<pre><code># 실행 중인 프로세스 프로파일링
py-spy record -o profile.svg --pid 12345
# 스크립트 실행과 동시에 프로파일링
py-spy record -o profile.svg -- python my_script.py</code></pre><p><strong>출력 예시 :</strong></p>
<blockquote>
</blockquote>
<pre><code>Sampling process 100 times a second. Press Control-C to exit.
Total Samples 1000
GIL: 89.00%, Active: 11.00%, Threads: 1
  %Own   %Total  OwnTime  TotalTime  Function (filename:line)
  89.00%  89.00%   0.89s     0.89s   slow_function (main.py:15)
   8.00%  97.00%   0.08s     0.97s   main (main.py:5)</code></pre><h3 id="python--m-cprofile--s-cumulative">python -m cProfile -s cumulative</h3>
<p><strong>목적 :</strong> 함수 레벨에서의 성능 병목 식별
<strong>사용 용도 :</strong> 파이썬 내장 프로파일러로 함수별 실행 시간 분
<strong>사용시 장점 :</strong></p>
<ul>
<li>별도 설치 불필요 (파이썬 내장)</li>
<li>정확한 함수 호출 통계 제공</li>
<li>다양한 정렬 옵션 제공</li>
</ul>
<p><strong>사용 예시 :</strong></p>
<blockquote>
</blockquote>
<pre><code class="language-python"># 스크립트 프로파일링
python -m cProfile -s cumulative my_script.py
# 결과를 파일로 저장
python -m cProfile -o profile.stats my_script.py</code></pre>
<p><strong>출력 예시 :</strong></p>
<blockquote>
</blockquote>
<pre><code> ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     16/1    0.000    0.000   43.451   43.451 {built-in method builtins.exec}
        1    0.000    0.000   43.451   43.451 my_script.py:1(&lt;module&gt;)
        1    0.000    0.000   43.424   43.424 my_script.py:518(main)
        1    0.000    0.000   42.251   42.251 my_script.py:396(run)
        6   39.477    6.579   39.477    6.579 {built-in method builtins.input}</code></pre><h2 id="5테스팅">5.테스팅</h2>
<h3 id="pip-install-pytest">pip install pytest</h3>
<p><strong>목적 :</strong> 코드 품질 보장 및 리그레션 방지
<strong>사용 용도 :</strong> 파이썬 코드의 단위 테스트, 통합 테스트 작성 및 실행
<strong>사용시 장점 :</strong></p>
<ul>
<li>간결한 테스트 코드 작성</li>
<li>강력한 fixture 시스템</li>
<li>풍부한 플러그인 생태계</li>
</ul>
<p><strong>사용 예시 :</strong></p>
<blockquote>
<pre><code class="language-python"></code></pre>
</blockquote>
<h1 id="테스트-실행">테스트 실행</h1>
<p>pytest</p>
<h1 id="커버리지와-함께-실행">커버리지와 함께 실행</h1>
<p>pytest --cov=src tests/</p>
<pre><code>
**출력 예시 :**
&gt;```
================================= test session starts =================================
platform darwin -- Python 3.9.7, pytest-7.1.2, pluggy-1.0.0
rootdir: /Users/project
plugins: cov-3.0.0
collected 8 items
tests/test_main.py::test_add_function PASSED                              [ 12%]
tests/test_main.py::test_subtract_function PASSED                         [ 25%]
tests/test_utils.py::test_validate_input PASSED                           [ 37%]
tests/test_utils.py::test_process_data FAILED                             [ 50%]
tests/test_api.py::test_get_user PASSED                                   [ 62%]
tests/test_api.py::test_create_user PASSED                                [ 75%]
tests/test_api.py::test_delete_user PASSED                                [ 87%]
tests/test_integration.py::test_full_workflow PASSED                      [100%]
================================== FAILURES ===================================
_________________________ test_process_data ________________________
    def test_process_data():
        result = process_data([1, 2, 3])
&gt;       assert result == [2, 4, 6]
E       assert [1, 4, 9] == [2, 4, 6]
E         At index 0 diff: 1 != 2
tests/test_utils.py:15: AssertionError
========================= short test summary info ==========================
FAILED tests/test_utils.py::test_process_data - assert [1, 4, 9] == [2, 4, 6]
========================= 1 failed, 7 passed in 0.12s =========================</code></pre><h3 id="pip-install-coverage">pip install coverage</h3>
<p><strong>목적 :</strong> 테스트의 완성도 평가
<strong>사용 용도 :</strong> 테스트가 코드의 어느 부분을 실행했는지 측정
<strong>사용시 장점 :</strong></p>
<ul>
<li>정확한 커버리지 측정</li>
<li>HTML 리포트 생성</li>
<li>브랜치 커버리지 지원</li>
</ul>
<p><strong>사용 예시 :</strong></p>
<blockquote>
<pre><code class="language-python"></code></pre>
</blockquote>
<h1 id="커버리지-실행">커버리지 실행</h1>
<p>coverage run -m pytest
coverage report
coverage html</p>
<pre><code>
**출력 예시 :**
&gt;```
Name                 Stmts   Miss  Cover   Missing
--------------------------------------------------
src/__init__.py          0      0   100%
src/main.py             45      3    93%   78-80
src/utils.py            32      8    75%   45-52, 67
src/api.py              28      0   100%
src/database.py         67     15    78%   89-95, 102-108, 125
--------------------------------------------------
TOTAL                  172     26    85%
Coverage HTML written to dir htmlcov</code></pre><h2 id="6의존성-관리">6.의존성 관리</h2>
<h3 id="curl--ssl-httpsinstallpython-poetryorg--python3--">curl -sSL <a href="https://install.python-poetry.org">https://install.python-poetry.org</a> | python3 -</h3>
<p><strong>목적 :</strong> 프로젝트 의존성의 체계적 관리
<strong>사용 용도 :</strong>  파이썬 프로젝트의 의존성 관리 및 패키징
<strong>사용시 장점 :</strong></p>
<ul>
<li>의존성 해결 알고리즘으로 충돌 방지</li>
<li>가상환경 자동 관리</li>
<li>배포용 패키지 빌드 지원</li>
</ul>
<p><strong>사용 예시 :</strong></p>
<blockquote>
<pre><code>&quot;&quot;&quot; 설치 &quot;&quot;&quot;
curl -sSL https://install.python-poetry.org | python3 -
&quot;&quot;&quot; 새 프로젝트 생성 &quot;&quot;&quot;
poetry new my-project
&quot;&quot;&quot; 의존성 설치 &quot;&quot;&quot;
poetry add requests
poetry add pytest --group dev</code></pre></blockquote>
<pre><code></code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[zed ai 윈도우 설치 및 세팅+ClaudeCode 리눅스 설치 및 세팅]]></title>
            <link>https://velog.io/@path_creator/zed-ai-%EC%9C%88%EB%8F%84%EC%9A%B0-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%84%B8%ED%8C%85</link>
            <guid>https://velog.io/@path_creator/zed-ai-%EC%9C%88%EB%8F%84%EC%9A%B0-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%84%B8%ED%8C%85</guid>
            <pubDate>Tue, 10 Jun 2025 08:35:32 GMT</pubDate>
            <description><![CDATA[<h4 id="zed-ai는-현재-미국-커뮤니티에서-가장-빠른-ai-코드-에디터로-소문나-있다">zed ai는 현재 미국 커뮤니티에서 가장 빠른 ai 코드 에디터로 소문나 있다.</h4>
<h4 id="claude-code는-터미널에서-활용-가능한-agent이고-현재-함수-단위의-코드를-제일-잘-만들어-주는걸로-알려져-있다---2505월-기준--">claude code는 터미널에서 활용 가능한 agent이고 현재 함수 단위의 코드를 제일 잘 만들어 주는걸로 알려져 있다. - 25.05월 기준 -</h4>
<h3 id="순서대로-다운받을-리스트">순서대로 다운받을 리스트</h3>
<p>설명 따라가기 이전 아래 순서대로 미리 다운받기.
<strong>1. 비쥬얼스튜디오 C++ 커뮤니티 추천(for zed)</strong>
<a href="https://visualstudio.microsoft.com/ko/downloads/">https://visualstudio.microsoft.com/ko/downloads/</a>
<strong>2. 러스트(for zed)</strong>
<a href="https://www.rust-lang.org/tools/install">https://www.rust-lang.org/tools/install</a>
<strong>3. 윈도우 개발자 키트(for zed)</strong>
<a href="https://developer.microsoft.com/ko-kr/windows/downloads/windows-sdk/">https://developer.microsoft.com/ko-kr/windows/downloads/windows-sdk/</a>
<strong>4. cmake(for zed)</strong>
<a href="https://cmake.org/download/">https://cmake.org/download/</a></p>
<p><strong>5. 리눅스 설치 이후, docker 통합화(for claude)</strong>
<a href="https://somaz.tistory.com/210">https://somaz.tistory.com/210</a></p>
<p><strong>6. mysys 설치(zed 설치 도구)</strong>
<a href="https://www.msys2.org/">https://www.msys2.org/</a></p>
<p>아래처럼 비쥬얼 스튜디오 installer에서 <strong>수정</strong> 
<img src="https://velog.velcdn.com/images/path_creator/post/b278dcbb-a6e0-498a-84aa-fde7efd2cd5a/image.png" alt="">
-&gt; <strong>개별 구성 요소</strong> 설정
<img src="https://velog.velcdn.com/images/path_creator/post/83909ffe-25d4-4f58-bcb7-08aabe2697f3/image.png" alt=""></p>
<p><strong>도커와 우분투 가상화 설정</strong>
<img src="https://velog.velcdn.com/images/path_creator/post/bf95afa2-ad57-4b74-953c-90125696aab6/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/path_creator/post/6a3d0892-4220-401a-b4d1-abfd9f8a2c4f/image.png" alt=""></p>
<h3 id="claude-code-설치-및-세팅">claude code 설치 및 세팅</h3>
<h4 id="우분투-설치-및-클로드-코드-적용">우분투 설치 및 클로드 코드 적용</h4>
<blockquote>
<p>wsl --list --verbose
<img src="https://velog.velcdn.com/images/path_creator/post/fb17afa6-c7b6-4910-8c04-3c42405b9d70/image.png" alt=""></p>
</blockquote>
<p><strong>우분투 접속 실행</strong>
라인의 내용이 변하면서 우분투 접속</p>
<blockquote>
<p>wsl -d Ubuntu-22.04
<img src="https://velog.velcdn.com/images/path_creator/post/4f3f995e-a11d-458b-b851-c258435b3b44/image.png" alt=""></p>
</blockquote>
<p>CURL은 서버와 통신할 수 있는 커맨드 명령어 툴이자 웹개발에 매우 많이 사용되고 있는 무료 오픈소스다. 우분투에 CURL 을 설치한다.</p>
<blockquote>
<p>$ sudo apt-get install -y curl</p>
</blockquote>
<p><strong>미리 apt 도구 업데이트 (우분투 안에서)</strong></p>
<blockquote>
<p>sudo apt update</p>
</blockquote>
<p><strong>node 20.x 버젼 설치 (우분투 안에서)</strong></p>
<blockquote>
<p>curl -sL <a href="https://deb.nodesource.com/setup_20.x">https://deb.nodesource.com/setup_20.x</a> | sudo bash -E -</p>
</blockquote>
<blockquote>
<p>sudo apt install -y nodejs</p>
</blockquote>
<p><strong>node 확인 (우분투 안에서)</strong></p>
<blockquote>
<p>nodejs -v </p>
</blockquote>
<p><strong>npm 설치 (우분투 안에서)</strong></p>
<blockquote>
<p>sudo apt install npm</p>
</blockquote>
<p><strong>npm 확인 (우분투 안에서)</strong></p>
<blockquote>
<p>npm -v</p>
</blockquote>
<p><strong>sudo로 claude-code 전역 설치 (폴더위치 제한없이 활용 가능한 구도) (우분투 안에서)</strong></p>
<blockquote>
<p>sudo npm install -g @anthropic-ai/claude-code</p>
</blockquote>
<p><strong>프로젝트 디렉토리로 이동 (우분투 안에서)</strong></p>
<blockquote>
<p>cd your-project-directory </p>
</blockquote>
<p><strong>클로드코드 실행 (우분투 안에서)</strong></p>
<blockquote>
<p>claude</p>
</blockquote>
<h4 id="-클로드로-실행-안될-경우">!! 클로드로 실행 안될 경우</h4>
<p><strong>오류 화면</strong>
<img src="https://velog.velcdn.com/images/path_creator/post/043141ab-8b92-4909-9c9a-733f87e879d3/image.png" alt="">
<strong>해결법</strong></p>
<ol>
<li>anthropic에서 클로드 로그인</li>
<li>터미널에서 다시 claude 입력</li>
</ol>
<h4 id="클로드-정상-작동될-경우">클로드 정상 작동될 경우</h4>
<p>클로드 구독이되 있거나, 클로드 api 비용 충전되 있어야함. 
<img src="https://velog.velcdn.com/images/path_creator/post/a3cb3166-d2c6-4357-bdd0-3cb31fdd07c5/image.png" alt=""></p>
<p>아래같이 화면이 뜨면 링크로 들어가서 
<img src="https://velog.velcdn.com/images/path_creator/post/e73e0b84-22f3-48f4-bbb2-f586c952dc3c/image.png" alt=""></p>
<p>아래창을 승인 후에 키코드 받은 이후, 키코드 터미널의 클로드에 입력
<img src="https://velog.velcdn.com/images/path_creator/post/ac1474e7-654e-45a7-82cd-5302ec6c216b/image.png" alt=""></p>
<h4 id="claude-code가-주로-작동되지-않는-인증-문제">claude code가 주로 작동되지 않는 인증 문제</h4>
<p>간혹 아래와 같은 코드가 뜨면서 작동이 안될 경우 존재</p>
<blockquote>
</blockquote>
<pre><code>API Error: 401 {&quot;type&quot;:&quot;error&quot;,&quot;error&quot;:{&quot;type&quot;:&quot;authentication_error&quot;,&quot;message&quot;:&quot;OAuth token has expired. Please obtain a new token or refresh your existing token.&quot;}}</code></pre><p><strong>인증 문제 해결법</strong></p>
<ul>
<li>claude code를 키고, /login 을 쳐서 재접속시 다시 해결 </li>
<li>추가 정보 : /help를 입력시 어떤 command들이 존재하는지 나열함 </li>
</ul>
<h4 id="클로드코드-rules프롬프트-세팅">클로드코드 rules(프롬프트 세팅)</h4>
<p>언어별로 <strong>디렉토리를 나누어 관리</strong>
<img src="https://velog.velcdn.com/images/path_creator/post/a8768708-ed8e-428a-8c5e-01714e2a8c1e/image.png" alt=""></p>
<ol>
<li>위 사진 처럼 <strong>.claude</strong>라는 디렉토리 생성</li>
<li><strong>.claude</strong>에서 <strong>rules.md</strong> 생성 </li>
<li>프롬프트 지시 사항 입력</li>
</ol>
<p>사용했던 <strong>전략적 프롬프트</strong>이며 기준은 <strong>파인썬 언어</strong> 기준</p>
<blockquote>
<pre><code></code></pre></blockquote>
<h1 id="role">Role</h1>
<p>Professional software engineer developing production-ready services with clean architecture, applying SOLID principles, GRASP patterns, GoF Design Patterns, and functional programming concepts optimized for automated development environments.</p>
<ul>
<li>Target : Python 3.9+ features and syntax</li>
<li>Implement proper generic types where applicable<h1 id="core-development-standards">Core Development Standards</h1>
<h2 id="error-handling--robustness">Error Handling &amp; Robustness</h2>
</li>
<li>Implement comprehensive exception handling with specific exception types</li>
<li>Use context managers for resource management</li>
<li>Validate input parameters and return meaningful error messages</li>
<li>Log errors with appropriate severity levels<h2 id="performance--optimization">Performance &amp; Optimization</h2>
</li>
<li>Profile code performance for critical paths</li>
<li>Use appropriate data structures and algorithms</li>
<li>Optimize for memory efficiency when handling large datasets<h2 id="security-considerations">Security Considerations</h2>
</li>
<li>Use environment variables for sensitive configuration</li>
<li>Implement proper authentication and authorization patterns</li>
<li>Avoid hardcoded secrets and credentials<h1 id="code-quality-guidelines">Code Quality Guidelines</h1>
<h2 id="structure--maintainability">Structure &amp; Maintainability</h2>
</li>
<li>Single Responsibility: One function, one clear purpose</li>
<li>Keep functions under 20 lines when possible</li>
<li>Use descriptive variable and function names (no abbreviations)</li>
<li>Import Organization: standard library → third-party → local imports</li>
<li>Group related functionality into cohesive modules</li>
<li>Apply appropriate design patterns (Factory, Observer, Strategy, etc.)<h2 id="documentation--comments">Documentation &amp; Comments</h2>
</li>
<li>Write comprehensive docstrings for all public functions and classes</li>
<li>Include parameter types, return types, and example usage in docstrings</li>
<li>Add inline comments for complex algorithms and business logic</li>
<li>Document design decisions and architectural choices<h2 id="code-style--formatting">Code Style &amp; Formatting</h2>
</li>
<li>Follow PEP 8 style guidelines strictly</li>
<li>Use consistent naming conventions (snake_case for functions/variables, PascalCase for classes)</li>
<li>Maintain line length under 88 characters</li>
<li>Use meaningful type hints throughout the codebase<h1 id="development-workflow">Development Workflow</h1>
<h2 id="code-quality-verification">Code Quality Verification</h2>
</li>
<li>Execute linting with ruff or pylint</li>
<li>Format code with black and isort</li>
<li>Verify no security vulnerabilities with bandit</li>
<li>Run static type checking with mypy<h2 id="testing-strategy-context-dependent">Testing Strategy (context-dependent)</h2>
<h3 id="for-production-services">For Production Services:</h3>
</li>
<li>Achieve minimum 80% test coverage</li>
<li>Write unit tests for all core business logic</li>
<li>Include integration tests for external dependencies</li>
<li>Use fixtures for test data and mocking external services<h3 id="for-simple-scripts">For Simple Scripts:</h3>
</li>
<li>Focus on error handling and input validation</li>
<li>Basic functional testing when needed</li>
<li>Documentation over extensive testing<h2 id="deployment-readiness">Deployment Readiness</h2>
</li>
<li>Verify all dependencies are properly declared</li>
<li>Test pip package detection and installation</li>
<li>Ensure both CLI and interactive modes function correctly</li>
<li>Validate environment variable configuration</li>
<li>Perform dependency vulnerability scanning<h1 id="development-tools-optional">Development Tools (Optional)</h1>
Formatting: black(코드 포매터), isort(import한거 분야별 자동 정렬)
Linting: ruff(초고속 린터 코드 스타일 분석), pylint(종합 분석 도구)
Security: bandit(보안 취약점 스캐너) 
Performance: py-spy(프로세스 성능 분석), memory-profiler(메모리 사용량 분석)
Documentation: mkdocs(간편한 마크다운 문서화)<h1 id="context-specific-applications">Context-Specific Applications</h1>
<h2 id="simple-scripts--prototypes">Simple Scripts &amp; Prototypes</h2>
</li>
<li>Prioritize: Type hints, error handling, clean structure</li>
<li>Optional: Testing, comprehensive documentation</li>
<li>Focus: Quick implementation with maintainable code<h2 id="production-services">Production Services</h2>
</li>
<li>Apply all guidelines strictly</li>
<li>Full documentation and performance optimization</li>
<li>Implement monitoring and logging strategies<h2 id="data-science-projects">Data Science Projects</h2>
</li>
<li>Emphasize: Data validation, reproducible results</li>
<li>Use appropriate libraries (pandas, numpy, scikit-learn)</li>
<li>Document data processing steps and assumptions</li>
<li>Handle missing values and edge cases gracefully<pre><code></code></pre></li>
</ul>
<h3 id="zed-ai-설치-및-세팅">ZED ai 설치 및 세팅</h3>
<h4 id="cmake-폴더-위치">cmake 폴더 위치</h4>
<p>C:\Program Files\CMake 폴더를 아래 디렉토리 위치로 옮김</p>
<h4 id="cmake-폴더-아래-microsoft-아래로-이동">cmake 폴더 아래 ..\Microsoft 아래로 이동</h4>
<p>C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\CommonExtensions\Microsoft\</p>
<p>C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE\CommonExtensions\Microsoft\CMake</p>
<p><strong>좌측 하단 윈도우즈 버트 클릭-&gt; MSYS2 MINGW64 실행</strong>
<img src="https://velog.velcdn.com/images/path_creator/post/8abce3cf-bc46-4b82-a90f-8c7da88f29d3/image.png" alt=""></p>
<p><strong>MSYS2 MINGW64</strong> 열어서 아래 입력</p>
<blockquote>
</blockquote>
<p>pacman -Syu
pacman -S $MINGW_PACKAGE_PREFIX-zed</p>
<p>MINGW 터미널 창에서 Y를 눌 모두 동의</p>
<p>모든 설치 완료 후, zed ai 실행</p>
<blockquote>
</blockquote>
<p>export PATH=&quot;/mingw64/bin:$PATH&quot;
zeditor</p>
<p>처음엔 LLM모델 설정, 창 설정 등이 필요
<img src="https://velog.velcdn.com/images/path_creator/post/85d971b1-50cc-40bc-a651-455fb13cf9b2/image.png" alt=""></p>
<h4 id="msys2에서-pip-이나-git-필요-도구-설치">MSYS2에서 pip 이나 git 필요 도구 설치</h4>
<p>MSYS2 mingw-w64가 기반이라서 따로 도구 설치 필요
터미널이나 powershell에서 여태 설치된건 MSYS2에 적용 <strong>X</strong></p>
<blockquote>
<p>pacman -S mingw-w64-x86_64-python-pip
pacman -S git</p>
</blockquote>
<p>아래 default 설치로 필요한 기타 개발 통합 도구들을 설치</p>
<blockquote>
<p>pacman -S mingw-w64-x86_64-toolchain</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/path_creator/post/30ad812b-18b0-4103-af26-ac11b9979235/image.png" alt=""></p>
<ol>
<li>상단에 <strong>view</strong> -&gt; <strong>toggle Right</strong> --&gt; 우측 상단에 <strong>&quot;...&quot;</strong> 클릭 </li>
<li>rules에 프롬프트를 추가 후 -&gt; Add default rules</li>
</ol>
<h3 id="클로드code와-제드-함께-활용하기">클로드code와 제드 함께 활용하기</h3>
<p><strong>!!주의!!</strong> : 클로드코드는 단순히 터미널안에 디렉토리에서 코드를 파악하고 수정하는 방식이기 때문에 이전에 zed ai에서 학습한 질문/답 데이터를 claude code와 함께 공유하지 않는다.</p>
<p><img src="https://velog.velcdn.com/images/path_creator/post/e6b61d18-2606-480f-b1ff-c8720e0dde14/image.png" alt=""></p>
<p>아래처럼 zed 터미널에 입력, 우분투 활성화</p>
<blockquote>
<p>PS C:\Users\USER\Documents\github\python&gt; wsl -d Ubuntu-22.04</p>
</blockquote>
<p>클로드 실행</p>
<blockquote>
<p>han<strong>***</strong>@DESKTOP-04KQO1M:/mnt/c/Users/USER/Documents/github/python$ claude</p>
</blockquote>
<h3 id="비용-문제">비용 문제</h3>
<ul>
<li><p>zed ai 무료 버젼은 llm api 키 사용 때문에 비용 발생 -- 약 6시간에 일반인 바이브 코딩이 약1.5불 사용 (클로드 4.0 llm 사용시)</p>
</li>
<li><p>Claude code는 claude 구독을 한다면 추가 비용 발생 없음</p>
</li>
</ul>
<h4 id="따라서-비용-전략면에서-사용한다면">따라서 비용 전략면에서 사용한다면</h4>
<ol>
<li>app의 디렉토리를 일반 ai(일반 클로드, gpt 등등)로 모두 구성</li>
<li>초반 틀을 잡는 개발은 각 디렉토리별로 분리해서 Claude code 사용</li>
<li>다음 전체적인 유동적 코딩과 클래스와 클래스, 디렉토리와 디렉토리 사이 코딩은 zed ai를 활용</li>
<li>함수쪽 문제 개선이나 현재 코드 자체의 문제 해결력은 Claude code가 더 월등히 좋아 Claude code를 사용</li>
</ol>
]]></description>
        </item>
        <item>
            <title><![CDATA[프롬프트 엔지니어링 인사이트[1]]]></title>
            <link>https://velog.io/@path_creator/%ED%94%84%EB%A1%AC%ED%94%84%ED%8A%B8-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4%EB%A7%81-%EC%9D%B8%EC%82%AC%EC%9D%B4%ED%8A%B81</link>
            <guid>https://velog.io/@path_creator/%ED%94%84%EB%A1%AC%ED%94%84%ED%8A%B8-%EC%97%94%EC%A7%80%EB%8B%88%EC%96%B4%EB%A7%81-%EC%9D%B8%EC%82%AC%EC%9D%B4%ED%8A%B81</guid>
            <pubDate>Mon, 02 Jun 2025 04:53:52 GMT</pubDate>
            <description><![CDATA[<h3 id="intro">Intro</h3>
<p>프롬프트 문제 두개 예제들을 풀이하며 알게된 점을 일지로 작성하였습니다.</p>
<ul>
<li>맨아래에는 프롬프트 작성 팁을 첨부하였습니다.</li>
</ul>
<h2 id="첫번째-문제">첫번째 문제</h2>
<p><strong>Q : 아래 글을 읽고 여기서 지역을 의미하는 단어만 뽑아 내라</strong></p>
<p>&quot;음바페는 26일 쿠프 드 프랑스㏙프랑스컵㏚ 올랭피크 리옹과의 결승전에서 풀타임을 뛰며 2㏊1로 팀 승리를 도왔다. 3년 만에 대회 정상에 오른 PSG는 역대 최다 15회 우승으로 2위 마르세유㏙10회㏚를 멀찍이 따내 도시인 리옹, 마르세유 등이 있으면 반드시 포함하라.돌렸다. 음바페는 이날 경기로 PSG에서의 커리어를 마무리했다. 음바페는 7시즌 동안 공식전 308경기 256골의 성적을 남기고 PSG를 떠난다.&quot;</p>
<p>model : gpt 3.5 turbo
<strong>프롬프트 정답</strong></p>
<blockquote>
</blockquote>
<p>system
프롬프트에 없는 단어는 뽑지마라
#명령
아래 &quot;예시&quot;에서 국가 또는 도시명만 뽑아서 출력하라.
다른 모든 단어는 무시하고, 지리적 위치 국가/도시만 추출하라.
#예시
&quot;음바페는 26일 쿠프 드 프랑스㏙프랑스컵㏚ 올랭피크 리옹과의 결승전에서 풀타임을 뛰며 2㏊1로 팀 승리를 도왔다. 3년 만에 대회 정상에 오른 PSG는 역대 최다 15회 우승으로 2위 마르세유㏙10회㏚를 멀찍이 따내 도시인 리옹, 마르세유 등이 있으면 반드시 포함하라.돌렸다. 음바페는 이날 경기로 PSG에서의 커리어를 마무리했다. 음바페는 7시즌 동안 공식전 308경기 256골의 성적을 남기고 PSG를 떠난다.&quot;</p>
<h2 id="1번-문제-프롬프트-일지-">1번 문제 프롬프트 일지 :</h2>
<h3 id="언어나-문장-구성적-요소">언어나 문장 구성적 요소</h3>
<h4 id="반복-문구의-문제">반복 문구의 문제</h4>
<ul>
<li>처음에는 [#명령 #상황 #제한 사항 #예시]로 문장을 구성하여, 만들었으나 문제가 많았다. [#명령과 #예시]를 제외하면 주로 제한적인 요구 사항등을 넣었습니다. 하지만 반복되는 문구가 많아서인지 psg나 있지도 않은 파리를 뽑는 할루시네이션이 많았다. (억지로 더 뽑는다는 느낌이었습니다.)</li>
<li><ul>
<li>프롬프트입력 량이 많으면 출력량도 많아지는 경향이 있음.**</li>
</ul>
</li>
</ul>
<p><strong>나의 가설과 추정 :</strong>
아무래도 절차 지향이다 보니 겹쳐진 정보를 읽는 부분에서 약간의 충돌성이 있는게 아닐까 생각된다.</p>
<h4 id="문장의-위치적-지정과-인식의-방법">문장의 위치적 지정과 인식의 방법</h4>
<ul>
<li>프롬프트가 정보를 다읽고 판단하여 답변을 하는지 vs 절차적으로 답변을 읽고 판단하는지라고 본다면? </li>
</ul>
<p>작업을 해본 결과 <strong>&quot;[아래의 &quot;예시&quot;]&quot;</strong>라고 언급할때와 단순히 <strong>[&quot;예시&quot;]</strong>를 언급하는 차이에서 여러 형태의 차이가 발견됬다. </p>
<p><strong>나의 가설과 추정 : **
이로써 프롬프트는 **절차적 사고</strong>(순서대로 읽으면서 정보를 판단)를 하는 걸로 추정된다. 그렇다면 앞으로 참고 정보와 명령등을 할때 * 단순 지칭*보다 * 위치를 언급 *하여 사고하도록 유도해야 한다.</p>
<h4 id="단어의-형태나-용례-1">단어의 형태나 용례 1</h4>
<ul>
<li>처음에는 지역적인 부분만 뽑으면 된다고 생각하여 단순히 &quot;예시&quot;안에 place 또는 location이라는 단어를 사용했다 하지만 그렇게 사용하면 [프랑스, 리옹]은 뽑혀도 [마르세유]는 뽑지 않았다. [마르세유, 리옹]은 둘다 도시이다, 하지만, 왜 &quot;장소, 지역 도시만&quot; 이라는 형태로 바꾸니 마르세유가 뽑혔을까?!</li>
</ul>
<p><strong>나의 가설과 추정 :  **
아무래도 대량 언어와 context를 학습된 생성형 ai는 이미 **온라인에서 올려진 정보를 기반</strong>으로 맥락을 파악하는 경향이 있는걸로 추정이 된다. place나 location 역시 도시라는 개념을 포함하지만, 정보에서 이 단어를 place나 location과 연결하여 설명한 정보들은 [프랑스, 리옹]이 많았다. 반대로, [마르세유]는 도시와 연결한 설명이 많았다. 그 부분에서 연관성으로 거르는게 아닌가? 라는생각이 든다.</p>
<h4 id="단어의-형태나-용례-2">단어의 형태나 용례 2</h4>
<ul>
<li>&quot;지리적 위치 국가/도시만&quot;을  &quot;지리적 위치 국가나 도시만&quot; 추출하라 라고 했을 경우 psg(지역 아님)를 추가하여 답변한 경우가 있다. 왜 그럴까?</li>
</ul>
<p><strong>나의 가설과 추정 : **
이부분의 문제로 본다면 토큰을 읽는 부분에서 문제를 일으키거나 또는 **&quot;Or&quot;이란 단어가 열린 사고</strong>를 지향하기도 하고, gpt자체의 추론을 자극하는 걸로 추정된다.</p>
<h4 id="세팅">세팅</h4>
<p>P를 0.75로 설정하여 일관성 있게 답변이 나오도록 수렴 시켰다. --- p는 여러 수치로 바꾸다 보면 20개의 답변들중 1~3개의 답변들이 다른 답변을 내놓았다. 
다양한 답변을 언급하지 않도록 temperature는 0.46으로 맞추었다. --하지만 temp는 설정을 어떻게 바꾸든 일관된 답변들이 나오는 편이다. (100%는 아니다.)</p>
<p><strong>나의 가설과 추정 :</strong>
이런 행태로 보아서는 P는 설정에서 temperature보다는 <strong>영향력이 훨씬 강한</strong>걸로 보인다.
<img src="https://velog.velcdn.com/images/path_creator/post/734fcf16-49e1-4776-9045-12148381ac07/image.png" alt=""></p>
<h2 id="두번째-문제">두번째 문제</h2>
<p><strong>Q : 다음 아래 영문을 한글로 번역해라. 단, 전문 용어나 기술 용어는 영어로 남겨두고 &quot;( )&quot;안에 한국어를 써라 예: &quot;o1모델 시리즈는 대규모 reinforcement learning(강화 학습)과 훈련된다.&quot;</strong></p>
<p>&quot;The o1 model series is trained with large-scale reinforcement learning to reason using chain of thought. These advanced reasoning capabilities provide new avenues for improving the safety and robustness of our models. In particular, our models can reason about our safety policies in context when responding to potentially unsafe prompts. This leads to state-of-the-art performance on certain benchmarks for risks such as generating illicit advice, choosing stereotyped responses, and succumbing to known jailbreaks. Training models to incorporate a chain of thought before answering has the potential to unlock substantial benefits, while also increasing potential risks that stem from heightened intelligence. Our results underscore the need for building robust alignment methods, extensively stress-testing their efficacy, and maintaining meticulous risk management protocols. This report outlines the safety work carried out for the OpenAI o1-preview and OpenAI o1-mini models, including safety evaluations, external red teaming, and Preparedness Framework evaluations.&quot;</p>
<p>model : gpt_4o_mini
<strong>프롬프트 정답</strong></p>
<blockquote>
</blockquote>
<p>system
Given english texts, &quot;기술 용어 및 개념어들&quot;은 절대적으로 영어 원문을 유지해라. 영어 원문을 유지해야하는 &quot;기술 용어 및 개념어들&quot;의 각 단어 바로 뒤에 &quot;()&quot; 안에 변환된 한국어를 넣어라.
\n 그리고 영어 원문을 유지하는 단어들을 제외한 나머지는 한국어로 번역
user
&quot;The o1 model series is trained with large-scale reinforcement learning to reason using chain of thought. These advanced reasoning capabilities provide new avenues for improving the safety and robustness of our models. In particular, our models can reason about our safety policies in context when responding to potentially unsafe prompts. This leads to state-of-the-art performance on certain benchmarks for risks such as generating illicit advice, choosing stereotyped responses, and succumbing to known jailbreaks. Training models to incorporate a chain of thought before answering has the potential to unlock substantial benefits, while also increasing potential risks that stem from heightened intelligence. Our results underscore the need for building robust alignment methods, extensively stress-testing their efficacy, and maintaining meticulous risk management protocols. This report outlines the safety work carried out for the OpenAI o1-preview and OpenAI o1-mini models, including safety evaluations, external red teaming, and Preparedness Framework evaluations.&quot;</p>
<h2 id="2번-문제-프롬프트-일지-">2번 문제 프롬프트 일지 :</h2>
<h3 id="언어나-문장-구성적-요소-1">언어나 문장 구성적 요소</h3>
<h4 id="예외의-작용">예외의 작용</h4>
<ul>
<li>그 외, 예외, except등의 배제 구조롤 프롬프틑를 작성했을때는 잘 이행하지 못하는 경우가 있었다. 하지만, 문장들 간의 긴장감 차이가 있다면 작동이 잘 되기도 한다. 예를들어 위 답변의 프롬프트에서 &quot;그 외&quot; 바로 윗 문장의 프롬프트는 문장의 긴장감 자체가 높아 답변을 잘 찍어냈다.</li>
</ul>
<p>*<em>나의 가설과 추정 : *</em>
언어의 맥락(데이터의 벡터)의 형태가 같은 복수의 문장이 있다면 구분을 잘 못하는 경우가 있는거 같다. 따라서 문장의 긴장도와 차별성을 보여줌으로써 프롬프트에 맞는 답변을 만든다.</p>
<h4 id="문장의-긴장감과-차별화된-명령">문장의 긴장감과 차별화된 명령</h4>
<ul>
<li>지칭하는 <strong>단어 형태를 변형</strong>하면서 프롬프트를 쓰면 답변이 바뀌는 경향이 있다. 예를들어 이전 답변에서는 &quot;그 외 문장은&quot; 은 원래 &quot;그 외 english texts나&quot; , &quot;그 외 영어나 &quot;, &quot;그 외 english나&quot;등으로 사용 했었다. 하지만 그렇게 사용하게되면 결과는 예:&quot;대규모 강화 학습(large-scale reinforcement learning)&quot;-- 한글(영어)가 나오는 문제가 발생한다. 지칭한거와 다른 형태로 <strong>처리하기 편한식으로 나온다</strong>는 것이다. 하지만, 예: &quot;예:그리고 영어 원문을 유지하는 단어들&quot;이라는 이전에 언급되지 않았고 영어로도 <strong>언급된적 없는 강조된 단어를 사용한다면</strong> 답변은 &quot;reinforcement learning(강화 학습)&quot; 명령한데로 정확히나온다.</li>
</ul>
<p><strong>나의 가설과 추정 : **
언어에 대한 맥락과 **유사성이 다른 단어를 쓸때는 좀더 민감하게 명령을 읽는</strong>걸로 추정된다. 특히, 프롬프트에서 <strong>사용한적 없던 단어</strong>를 사용하는 것이 ai가 프롬프트의 명령을 더 잘 이행하는걸로 보인다.
<img src="https://velog.velcdn.com/images/path_creator/post/5f50ad8a-66c0-4b97-b22b-d33789c9ea0e/image.png" alt=""></p>
<h2 id="프롬프트-팁">프롬프트 팁</h2>
<h3 id="최적화된-문장-형태">최적화된 문장 형태</h3>
<p>•   ✅ 명령형 문장이 의문형보다 더 좋은 응답을 이끌어냅니다.</p>
<p>→ &quot;어떻게 만들 수 있을까?&quot; 보다 &quot;만들어줘&quot;가 더 명확합니다.</p>
<p>•   ✅ 보상을 암시하면 더 나은 답을 받는 경우가 많습니다.</p>
<p>→ &quot;팁을 줄게&quot;, &quot;좋은 결과를 낼 수 있어&quot; 같은 문구</p>
<p>•   ✅ Chain-of-Thought (CoT):</p>
<p>LLM에게 “순서대로 생각해봐”, “단계별로 문제를 해결해봐” 라고 유도하면 추론 정확도가 향상됨.</p>
<h3 id="특정-대상을-지칭할-경우">특정 대상을 지칭할 경우?</h3>
<p>&quot;입력 코드&quot;와 같은 지시어는 반드시 &quot;큰따옴표&quot;나 일반 서술형으로 사용하는 것이 가장 안정적입니다.</p>
<p><strong>고급 팁</strong></p>
<ul>
<li>프롬프트 시작 3줄이 답변 스타일을 거의 결정</li>
<li>&quot;역할&quot;과 &quot;상황&quot;이 명확하면, 모델은 훨씬 일관성 있게 행동</li>
<li>&quot;요구사항&quot;을 구체적으로 나누고 번호를 붙이면 정확도 20% 이상 올라감</li>
</ul>
<p><strong>괄호와 기호의 활용</strong> </p>
<ul>
<li>프롬프트 입력을 아끼고 좀더 명확한 의사전달 가능</li>
<li>프롬프트 구조화가 훨씬 수월해짐</li>
<li>프롬프트의 이해 능력 증진 가능</li>
<li>다른 형태의 언어와 섞어서 의사 전달 편리</li>
</ul>
<p>** 각 괄호 유형 비교 분석 **</p>
<table>
<thead>
<tr>
<th>괄호</th>
<th align="left">명칭</th>
<th align="center">GPT 내부 해석 용도</th>
<th align="left">추천 사용 맥락</th>
<th align="left">주의할 점</th>
<th align="right">사용 예시</th>
</tr>
</thead>
<tbody><tr>
<td>{ }</td>
<td align="left">중괄호</td>
<td align="center">템플릿 변수, 코드 블록</td>
<td align="left">코드 포맷팅, 변수 치환 영역</td>
<td align="left">❌ 프롬프트 본문에서는 혼란을 초래 (템플릿 엔진과 충돌)</td>
<td align="right">{username} 값을 URL에 삽입하세요.</td>
</tr>
<tr>
<td>[ ]</td>
<td align="left">대괄호</td>
<td align="center">리스트 항목, 선택지 또는 링크 표현</td>
<td align="left">옵션 표현, 항목 구분</td>
<td align="left">⚠️ 링크나 Markdown 표기와 혼동 가능</td>
<td align="right">조건 중 2개 이상을 만족해야 합니다: [가독성 개선], [네이밍 명확화], [성능 향상]</td>
</tr>
<tr>
<td>&lt; &gt;</td>
<td align="left">꺾쇠괄호</td>
<td align="center">HTML/XML 태그, 시스템 지시자</td>
<td align="left">명령어, API 응답 placeholder</td>
<td align="left">❌ LLM이 마크업 태그로 인식 가능성 높음</td>
<td align="right">응답은 <user_id>, <access_token> 형태입니다.</td>
</tr>
<tr>
<td>&quot; &quot;</td>
<td align="left">큰따옴표</td>
<td align="center">자연어 강조, 문자열 구분, 지시어 고정</td>
<td align="left">코드 지칭, 명확한 용어 설명</td>
<td align="left">✅ 가장 안정적인 텍스트 구분자</td>
<td align="right">&quot;입력 코드&quot;에서 오류를 찾으세요.</td>
</tr>
<tr>
<td>( )</td>
<td align="left">소괄호</td>
<td align="center">부가 설명, 선택 조건 설명</td>
<td align="left">부가 설명, 선택 조건 설명</td>
<td align="left">정보 보충, 예시 지시어, 조건 명시로 이해</td>
<td align="right">&quot;입력 코드&quot;(아래 블록 참조)에서 어떤 점을 개선할 수 있는가?</td>
</tr>
<tr>
<td>//</td>
<td align="left">파이프 괄호</td>
<td align="center">명시적 강조, 커스텀 지시어 구분</td>
<td align="left">⚠️ 다중 파이프 사용 시 Markdown 표 형식으로 인식될 수 있음</td>
<td align="left"></td>
<td align="right">입력 코드</td>
</tr>
</tbody></table>
<p>*<em>기호별 프롬프트 *</em></p>
<table>
<thead>
<tr>
<th>기호</th>
<th align="left">명칭</th>
<th align="center">GPT 내부 인식 용도</th>
<th align="left">추천 사용 전략</th>
<th align="left">주의사항</th>
<th align="right">예문</th>
</tr>
</thead>
<tbody><tr>
<td>$</td>
<td align="left">달러 기호</td>
<td align="center">변수, 금액, 수학 표현</td>
<td align="left">템플릿 변수, API 키, 수식 표현</td>
<td align="left">문자열 내 변수처럼 인식 가능</td>
<td align="right">코드에서 $username과 $password를 적절히 대체하세요. $API_KEY</td>
</tr>
<tr>
<td>%</td>
<td align="left">퍼센트</td>
<td align="center">수치 비교, 수식, 확률</td>
<td align="left">정확도/비율 설명 시 사용</td>
<td align="left">자연어로 percent보다 인식 잘 됨</td>
<td align="right">70% 정확도는 기준 이상입니다.</td>
</tr>
<tr>
<td>*</td>
<td align="left">별표</td>
<td align="center">Markdown 강조, 리스트 구분</td>
<td align="left">강조, 글머리 기호</td>
<td align="left">두 개 ** 이상 사용 시 bold</td>
<td align="right"><em>강조*된 개념과 리스트: *입력</em>, <em>검사</em>, <em>출력</em></td>
</tr>
<tr>
<td>-</td>
<td align="left">하이픈</td>
<td align="center">리스트, 마이너스, 구분선</td>
<td align="left">항목 나열, 마이너스 값 표현</td>
<td align="left">문단 내 연속 사용시 구분선 인식 가능</td>
<td align="right">단계별 실행: - 초기화 - 로드 - 검증</td>
</tr>
<tr>
<td>@</td>
<td align="left">앳 기호</td>
<td align="center">유저명, 변수, 경로 지시자</td>
<td align="left">이메일, 명령대상 식별자</td>
<td align="left">@GPT, @user 등으로 다중 객체 지시 가능</td>
<td align="right">@notification_service는 이벤트 발생 시 자동 호출됩니다.</td>
</tr>
</tbody></table>
<h3 id="예문">예문</h3>
<p>아래 &quot;입력 코드&quot;를 참조하고, (리팩토링 대상 함수)에서 개선할 수 있는 점을 말하시오.
또한 다음 조건 중 2개 이상을 만족해야 합니다: [가독성 개선], [네이밍 명확화], [성능 향상]
코드 내 {username}과 $token 값은 환경에 따라 동적으로 치환될 수 있습니다.
출력 형식은 다음과 같습니다:</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Kotlin mockk와 TDD 적용기]]></title>
            <link>https://velog.io/@path_creator/Kotlin-mockk%EC%99%80-TDD-%EC%A0%81%EC%9A%A9%EA%B8%B0-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@path_creator/Kotlin-mockk%EC%99%80-TDD-%EC%A0%81%EC%9A%A9%EA%B8%B0-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Sun, 16 Mar 2025 14:56:19 GMT</pubDate>
            <description><![CDATA[<h2 id="tdd-테스트-주도개발-일반적인-구성과-개념">TDD 테스트 주도개발 일반적인 구성과 개념</h2>
<h3 id="tdd가-필요한-이유와-역할">TDD가 필요한 이유와 역할</h3>
<ul>
<li>테스트 가능성을 고려한 설계는 클래스간 <strong>지나친 결합도를 점검 가능</strong></li>
<li><strong>가독성과 유지보수성</strong>을 높이기 위한 전략</li>
<li>코드의 결과에 대해 <strong>빠른 피드백 가능</strong></li>
<li>반복 테스트를 주기적으로 할 수 있는 구조로 개발을 이루어 가는 패턴</li>
</ul>
<h3 id="일반적인-설계-후-testcode-작업-플로우">일반적인 설계 후, TestCode 작업 플로우</h3>
<p>유닛 테스트 구성 -&gt; 통과 -&gt; 운영 코드 구성 -&gt; 통과 -&gt; 최소한의 코드로 원래 설계 재구성 -&gt; 유닛 테스트와 운영 코드 구성 -&gt; 통과 </p>
<h4 id="큰-틀에서-본-테스트-주도-설계-프로세스">큰 틀에서 본 테스트 주도 설계 프로세스</h4>
<p>** Red(실패하는 추상적 테스트 작성) → Green(빠르게 구현 후 테스트 통과) → Refactor(코드 리팩토링) 3단계**
위 사이클은 회귀 반복하는 사이클</p>
<ul>
<li>Arrange (준비) → 필요한 객체 생성 및 초기화</li>
<li>Act (실행) → 실제 테스트할 메서드 실행</li>
<li>Assert (검증) → 기대한 결과와 실제 결과 비교</li>
</ul>
<p><strong>테스트코드 함수의 구성은?</strong>
<strong>Given</strong>(주어진 정보) <strong>When</strong>(함수의 적용) <strong>then</strong>(이행된 함수의 평가) <strong>Verified</strong>(참,거짓 등의 입증)</p>
<p>아래는 예시 코드</p>
<blockquote>
</blockquote>
<pre><code class="language-kotlin">@Test
    fun `findByTaskName() - 존재하는 Task 반환`() = runTest {
        // Given
        val taskName = &quot;Test Task&quot;
        val mockTask = Task(id = 1L, taskName = taskName, difficulty = 3)
        coEvery { taskJpaRepository.findByTaskName(taskName) } returns mockTask
        // When
        val result = taskRepository.findByTaskName(taskName)
        // Then
        assertNotNull(result)
        assertEquals(taskName, result?.taskName)
        // Verify
        coVerify(exactly = 1) { taskJpaRepository.findByTaskName(taskName) }
    }</code></pre>
<p>이상적이라 생각하는 테스트 주도 개발의 <strong>TDD의 구체적인 프로세스</strong></p>
<ol>
<li>기본적인 Service에 해당하는 TestCode 구성 -&gt; 2. 단위별로 테스트구성 -&gt; 3. DAO 구성 -&gt; 4. DTO 구성 -&gt; 5. service 구성 -&gt;  6. 서비스에 관련된 운영 테스트 코드 구성 -&gt; 7. DAO 재구성 -&gt; 8. DTO 재구성 -&gt; 9. service 재구성 -&gt; 1. 번으로 돌아가 반복하며 재구성 </li>
</ol>
<h3 id="tdd테스트주도-개발의-생산성-저하를-보완하려면">TDD(테스트주도 개발)의 생산성 저하를 보완하려면?</h3>
<p><strong>TDD(테스트주도 개발)의 단점 *<em>: 첫 구성에선 다소 난해할 수 있으며 정작 핵심 코드 이전에 테스트코드로 틀을 짠후에 구성하는 부분에서 생산성이 떨어지며 구체적인 구성이 안된상태에서 짠다면 여러모로 개발 시간에 타격을 준다. 개발하는 사람의 관점에서 본다면 개발자의 성취감에 영향을 주는 지루한 작업이기도 하다. 따라서 *</em>최대한 반복작업을 줄이고 성취감을 빨리 이룰수 있는 전략을 시행해야한다.</strong></p>
<h4 id="1-atdd는-시스템-전-구간을-테스트하여-tdd-의존성에-대한-장애를-극복-가능">1. <strong>ATDD는 시스템 전 구간을 테스트</strong>하여 TDD 의존성에 대한 장애를 극복 가능</h4>
<p><img src="https://velog.velcdn.com/images/path_creator/post/f557edac-30b7-492b-95ea-7157ce02dac0/image.png" alt="">
<strong>Acceptance Test 란?</strong>
시스템 내부 코드를 가능한 한 직접 호출하지 말고 시스템 전 구간을 테스트해야 한다. 즉, end-to-end 테스트
인수 테스트는 보통 기능 테스트, 고객 테스트, 시스템 테스트와 같은 용어로도 사용된다.</p>
<h4 id="2-도메인에-따라-간단한-유닛테스트들로-구성하여-점차-통합-테스트로-발전">2. 도메인에 따라 간단한 유닛테스트들로 구성하여 점차 통합 테스트로 발전</h4>
<ul>
<li>빠르게 생각나는 방향에 맞춰 유닛 테스트코드들을 도메인에 맞게 구성</li>
<li>유닛 테스트를 기반으로 DAO 구성</li>
<li>유닛 테스트를 기반으로 DTO 구성</li>
<li>DAO와 DTO 리패토링 </li>
<li>DAO와 DTO의 통합 테스트 코드 작성</li>
<li>위와 같은 방식으로 service 다음 controller(추천은 안함) 순으로 TDD 순환을 돌리며 작업</li>
</ul>
<h4 id="3-하나의-테스트-코드의-함수로-여러가지-데이터를-테스트하지-않도록-최대한-분리">3. 하나의 테스트 코드의 함수로 여러가지 데이터를 테스트하지 않도록 최대한 분리</h4>
<ul>
<li>의존성이 강한 코드가 나오는 것을 예방하기 위해 최대한 <strong>분리하여 테스트 코드 함수 개발</strong></li>
<li>테스트 코드의 특징상 연쇄적인 코드이기에 <strong>의존성이 강해지는 것을 예방</strong>하기위해 데이터를 분리</li>
<li>혼동되는 코드나 <strong>비슷한 개념의 함수의 중복을 예방</strong>하기 위하여 분리</li>
</ul>
<p>아래는 같은 컨셉의 데이터의 다른 특징을 열거하며 한번에 분리된 코드 작성</p>
<blockquote>
</blockquote>
<pre><code class="language-kotlin">class RequirementsTest {
    @Nested
    @DisplayName(&quot;Requirements 변환 테스트&quot;)
    inner class ConversionTests {
        @Test
        fun `문자열을 리스트로 변환할 수 있다`() {
            // given
            val requirements = Requirements(&quot;A, B, C&quot;)  // 입력에 공백 포함
            // when
            val result = requirements.requirementsList
            // then
            assertThat(result).containsExactly(&quot;A&quot;, &quot;B&quot;, &quot;C&quot;)  // 결과는 trim된 값
        }
        @Test
        fun `공백이 있는 문자열도 올바르게 처리된다`() {
            // given
            val requirements = Requirements(&quot; A ,B  ,  C &quot;)
            // when
            val result = requirements.requirementsList
            // then
            assertThat(result).containsExactly(&quot;A&quot;, &quot;B&quot;, &quot;C&quot;)
        }
        @Test
        fun `빈 문자열은 빈 리스트로 변환된다`() {
            // given
            val requirements = Requirements(&quot;&quot;)
            // when
            val result = requirements.requirementsList
            // then
            assertThat(result).isEmpty()
        }
    }</code></pre>
<h3 id="mockk는-사용의-이유">mockk는 사용의 이유</h3>
<h4 id="mock은-언제-사용하는가">Mock은 언제 사용하는가?</h4>
<p>Mock은 테스트 대상 객체가 외부 의존성(DB, API, 파일 시스템 등)에 의존할 때, 실제 동작을 모방하여 독립적인 테스트를 가능하게 하는 기법</p>
<h4 id="mock을-사용하는-주요-상황">Mock을 사용하는 주요 상황</h4>
<p><strong>외부 시스템(데이터베이스, API, 파일 시스템 등)과 연동이 필요한 경우</strong></p>
<ul>
<li>실제 API 호출 없이 응답을 시뮬레이션</li>
<li>DB 연동 없이 데이터 조회를 테스트 가능</li>
</ul>
<p><strong>테스트 환경에서 직접 제어하기 어려운 경우</strong></p>
<ul>
<li>예를 들어, 현재 시간을 반환하는 함수(Mock을 이용하여 특정 시간을 설정 가능)</li>
<li>랜덤 값을 생성하는 함수(Mock을 이용하여 고정된 값 반환 가능)</li>
</ul>
<p><strong>비용이 큰 연산이 포함된 경우</strong></p>
<ul>
<li>대용량 데이터 처리, 복잡한 연산을 수행하는 로직이 있을 때 가짜 객체(Mock)를 사용하여 빠른 테스트 가능</li>
</ul>
<p><strong>독립적인 단위 테스트(Unit Test)를 수행해야 할 때</strong></p>
<ul>
<li>네트워크, DB와 같은 외부 의존성이 있는 객체 없이 순수한 비즈니스 로직만 테스트 가능</li>
</ul>
<blockquote>
</blockquote>
<h4 id="아래는-mockk를-활용한-전체적인-테스트-코드-구성">아래는 mockk를 활용한 전체적인 테스트 코드 구성</h4>
<pre><code class="language-kotlin">@SpringBootTest
class TaskServiceTest {
    private lateinit var taskService: TaskService
    private val taskRepository: TaskRepository = mockk()
    private val taskMapper: TaskMapper = mockk()
    private val tasksHistoryRepository: TasksHistoryRepository = mockk()
    @BeforeEach
    fun setUp() {
        taskService = TaskService(taskRepository, taskMapper, tasksHistoryRepository)
    }
    /** 공통 테스트 데이터를 생성하는 함수 */
    private fun setupMockTask(id: Long, name: String, role: String, difficulty: Int, requirement: String): Task {
        return Task(
            id = id,
            taskName = name,
            employeeRoles = EmployeeRoles(role),
            difficulty = difficulty,
            requirements = Requirements(requirement)
        )
    }
    private fun createResponseDTO(task: Task): TaskResponseDTO {
        return TaskResponseDTO(
            id = task.id,
            taskName = task.taskName,
            difficulty = task.difficulty,
            employeeRoles = task.employeeRoles.roles.map { it.displayName },
            requirements = task.requirements?.requirementsList ?: emptyList()
        )
    }
    @Test
    fun `findAllTasks should return list of TaskResponseDTO`() = runBlocking {
        // Given
        val tasks = listOf(
            setupMockTask(1L, &quot;Task 1&quot;, &quot;Manager&quot;, 3, &quot;Requirement 1&quot;),
            setupMockTask(2L, &quot;Task 2&quot;, &quot;Developer&quot;, 5, &quot;Requirement 2&quot;)
        )
        val taskDtos = tasks.map { createResponseDTO(it) }
        coEvery { taskRepository.findAll() } returns tasks
        every { taskMapper.toResponseDto(any()) } answers { createResponseDTO(firstArg()) }
        // When
        val result = taskService.findAllTasks()
        // Then
        assertEquals(taskDtos, result)
        coVerify { taskRepository.findAll() }
    }
    @Test
    fun `createTask should save and return TaskResponseDTO`() = runBlocking {
        // Given
        val request = TaskRequestDTO(&quot;New Task&quot;, 4, listOf(RoleType.DEVELOPER.toString()), listOf(&quot;Requirement A&quot;))
        val task = setupMockTask(1L, &quot;New Task&quot;, &quot;Developer&quot;, 4, &quot;Requirement A&quot;)
        val taskResponseDTO = createResponseDTO(task)
        every { taskMapper.toEntity(request) } returns task
        coEvery { taskRepository.save(task) } returns task
        every { taskMapper.toResponseDto(task) } returns taskResponseDTO
        // When
        val result = taskService.createTask(request)
        // Then
        assertEquals(taskResponseDTO, result)
        coVerify { taskRepository.save(task) }
    }
    @Test
    fun `updateTask should update and return updated TaskResponseDTO`() = runBlocking {
        // Given
        val request = TaskRequestDTO(&quot;Updated Task&quot;, 5, listOf(RoleType.DEVELOPER.toString()), listOf(&quot;Updated Requirement&quot;))
        val existingTask = setupMockTask(1L, &quot;Old Task&quot;, &quot;Developer&quot;, 3, &quot;Old Requirement&quot;)
        val updatedTask = setupMockTask(1L, &quot;Updated Task&quot;, &quot;Manager&quot;, 5, &quot;Updated Requirement&quot;)
        val taskResponseDTO = createResponseDTO(updatedTask)
        coEvery { taskRepository.findById(1L) } returns existingTask
        coEvery { taskRepository.save(any()) } returns updatedTask
        every { taskMapper.toResponseDto(updatedTask) } returns taskResponseDTO
        // When
        val result = taskService.updateTask(1L, request)
        // Then
        assertEquals(taskResponseDTO, result)
        coVerify { taskRepository.save(any()) }
    }
    @Test
    fun `deleteTask should remove task`() = runBlocking {
        // Given
        val task = setupMockTask(1L, &quot;Task to delete&quot;, &quot;Tester&quot;, 3, &quot;Test Req&quot;)
        coEvery { taskRepository.findById(1L) } returns task
        coEvery { taskRepository.delete(task) } just Runs
        // When
        taskService.deleteTask(1L)
        // Then
        coVerify { taskRepository.delete(task) }
    }
    @Test
    fun `findTaskById should return TaskResponseDTO if found`() = runBlocking {
        // Given
        val task = setupMockTask(1L, &quot;Find Task&quot;, &quot;Manager&quot;, 2, &quot;Req A&quot;)
        val taskResponseDTO = createResponseDTO(task)
        coEvery { taskRepository.findById(1L) } returns task
        every { taskMapper.toResponseDto(task) } returns taskResponseDTO
        // When
        val result = taskService.findTaskById(1L)
        // Then
        assertEquals(taskResponseDTO, result)
        coVerify { taskRepository.findById(1L) }
    }
    @Test
    fun `findTaskByName should return TaskResponseDTO if found`() = runBlocking {
        // Given
        val task = setupMockTask(1L, &quot;Task Name&quot;, &quot;Engineer&quot;, 2, &quot;Req B&quot;)
        val taskResponseDTO = createResponseDTO(task)
        coEvery { taskRepository.findByTaskName(&quot;Task Name&quot;) } returns task
        every { taskMapper.toResponseDto(task) } returns taskResponseDTO
        // When
        val result = taskService.findTaskByName(&quot;Task Name&quot;)
        // Then
        assertEquals(taskResponseDTO, result)
        coVerify { taskRepository.findByTaskName(&quot;Task Name&quot;) }
    }
}</code></pre>
<h3 id="tdd를-만들면서-피해야한다고-느낀점들">TDD를 만들면서 피해야한다고 느낀점들</h3>
<p>TDD(Test-Driven Development)는 강력한 개발 방법론이지만, 잘못된 방식으로 적용하면 오히려 비효율적인 개발 프로세스를 초래 합니다.</p>
<p>여기서 부터는 전적으로 이런 저런 글을 보고 배우며 느낀 제 개인 생각 입니다.</p>
<ul>
<li><strong>모든 코드에 대해 테스트</strong>를 작성하려는 강박, 단순한 코드는 테스트 코드까지 만들 이유는 없다. </li>
<li><strong>지나치게 상세한 구현에 의존</strong>하는 테스트 작성은 피한다.</li>
<li>Mock을 과도하게 사용하는 것을 피하여 <strong>코드의 일치성을 유지</strong>한다.</li>
<li><strong>추상적인 테스트 코드부터</strong> 접근하여 발전 시킨다.  </li>
</ul>
<p>TDD는 코드의 균형과 올바른 개발 도입부를 구성하기 위한 시작으로써 너무 많은 시간을 들여 짜기보다도 간단히 만들며 사이클을 돌리는 도구로써만 보면 된다고 생각이 된다. 켄트백이 말한 TDD가 많은 개발자들에게 TDD 강박증을 심은게 아닌가하는 생각도 든다. 우리는 도구로써 TDD를 접근하는데 옳다고 생각된다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[SNS tech] 대용량 트래픽 운용+광고 노출+ 커뮤니티 게시글 조회 운용 블로그]]></title>
            <link>https://velog.io/@path_creator/SNS-tech-%EB%8C%80%EC%9A%A9%EB%9F%89-%ED%8A%B8%EB%9E%98%ED%94%BD-%EC%9A%B4%EC%9A%A9%EA%B4%91%EA%B3%A0-%EB%85%B8%EC%B6%9C-%EC%BB%A4%EB%AE%A4%EB%8B%88%ED%8B%B0-%EA%B2%8C%EC%8B%9C%EA%B8%80-%EC%A1%B0%ED%9A%8C-%EC%9A%B4%EC%9A%A9-%EB%B8%94%EB%A1%9C%EA%B7%B8</link>
            <guid>https://velog.io/@path_creator/SNS-tech-%EB%8C%80%EC%9A%A9%EB%9F%89-%ED%8A%B8%EB%9E%98%ED%94%BD-%EC%9A%B4%EC%9A%A9%EA%B4%91%EA%B3%A0-%EB%85%B8%EC%B6%9C-%EC%BB%A4%EB%AE%A4%EB%8B%88%ED%8B%B0-%EA%B2%8C%EC%8B%9C%EA%B8%80-%EC%A1%B0%ED%9A%8C-%EC%9A%B4%EC%9A%A9-%EB%B8%94%EB%A1%9C%EA%B7%B8</guid>
            <pubDate>Tue, 04 Mar 2025 13:02:37 GMT</pubDate>
            <description><![CDATA[<h3 id="서비스-목표-">서비스 목표 :</h3>
<p><strong>콘텐츠 알람 및 광고 최적화등을 활용</strong>하여 커뮤니티 블로그의 전환율을 증진하는 서비스</p>
<ul>
<li>사용자의 특성에 따른 <strong>보안 관리 및 세션 보호</strong></li>
<li><strong>효율적인 실시간 알림</strong> 시스템 구축</li>
<li><strong>대용량 데이터 검색</strong> 및 성능 최적화</li>
<li>데이터 기반 컨텐츠 선정 및 <strong>광고 전환율 이력 관리</strong></li>
</ul>
<h3 id="1-서비스를-처음-구상하게-된-계기">1. 서비스를 처음 구상하게 된 계기</h3>
<p><img src="https://velog.velcdn.com/images/path_creator/post/617e87e7-1b08-426f-8d9c-0619c54e9809/image.png" alt=""></p>
<p><strong>사회적 분위기</strong>
특정 서비스들은 비용을 요구하기 보다 광고등으로 수익을 내는 서비스들이 많다. 유저의 접근성이 편한 서비스로써 최적인 것이다. 특히, 대용량 트래픽을 증진하고 광고 전환율을 증진 시키는 서비스는 수익과 직결된다. 이런 부분에 집중을 하고 이서비스를 만들게 되었다.</p>
<p><strong>계기</strong>
커뮤니티 블로그는 어디에나 존재하는 일반적인 아이디어이다. 하지만, 이를 서비스로 직접 구상하고 다양한 도구를 활용하여 대용량 데이터를 처리하는 경험을 쌓고 싶어 이 서비스를 접하게 되었다. 추가적으로 용의주도한 알고리즘을 활용하여 높은 접속률을 달성하기 위한 서비스도 개발하기 위해 이 프로젝트에 돌입 하였다.</p>
<h3 id="2-구상-방법">2. 구상 방법</h3>
<p><img src="https://velog.velcdn.com/images/path_creator/post/bcc3f810-6712-4de2-a0a4-74cbf5c7e6b2/image.png" alt=""></p>
<p><strong>해결의 목적</strong> - 대용량 트래픽을 빠르게 조회하고 처리하기 위한 커뮤니티 블로그 서비스로써 트래픽이 급증해도 안정적으로 동작하는 구조를 설계하는 것이 중요하다고 생각된다.
<strong>어떻게 해결 했는가?</strong> - 다양한 도구를 적용하고 코드를 만드는데 있어서 어려움이 있었다.</p>
<ul>
<li><strong>어려움 1</strong> 비동기 기능(WebFlux + Mono) 활용으로 대량의 요청을 구상하는데 있어서 데이터 정합성을 유지하는 방법의 어려움</li>
<li><strong>어려움 2</strong> ELK를 유동적으로 활용해서 실시간 로그 분석과 광고 데이터를 관리하는 구성의 어려움</li>
<li><strong>어려움 3</strong>
Elasticsearch를 활용하여 대량 데이터 실시간 검색 속도 증진하는 방법에 대한 구성의 어려움이 있었다.</li>
</ul>
<p><strong>어려움 1의 해결법</strong> </p>
<ul>
<li>비동기 기능(WebFlux + Mono) 활용으로 대량의 Redis 캐싱 활용으로 광고 조회 및 클릭 데이터를 Redis에 임시 저장하여 MySQL과 MongoDB에 대한 직접적인 부하를 줄였다. </li>
<li>@Scheduled를 활용하여 일정 주기로 Redis 데이터를 MySQL과 MongoDB로 동기화하였다. </li>
<li>WebFlux 환경에서 Reactor Context를 적용하여 요청별 트랜잭션 ID를 관리하였다. 데이터 정합성 유지 및 변환 오류 개선에 신경을 썻다.</li>
</ul>
<p><strong>어려움 2의 해결법</strong> </p>
<ul>
<li>불필요한 필드는 저장하지 않고, 필요한 데이터만 Logstash에서 선별하여 적재 하였다</li>
<li>광고 조회 이력 중 중요 필드만 유지하여 MongoDB와 Elasticsearch의 데이터 크기를 최적화 하였다
실시간 저장이 아닌, 일정 주기로 Batch 처리하여 MongoDB에 데이터 적재
JPA 기반의 정형 데이터와 연계하여 조회 속도를 향상 시켰다.</li>
</ul>
<p><strong>어려움 3의 해결법</strong></p>
<ul>
<li>Multi-Index를 활용하여 검색 성능 최적화 및 Db 테이블dmf full-text 형식으로 관리 하였다.</li>
<li>자주 검색되는 키워드는 Redis에 캐싱하여 Elasticsearch의 부하를 감소시켰다.</li>
<li>Elasticsearch의 샤드 수를 조정하여 데이터를 분산 저장 하였다.</li>
</ul>
<h3 id="3-그-외의-어려움">3. 그 외의 어려움?</h3>
<p><strong>여러 형태의 config 적용 및 분리형 클래스 전략</strong></p>
<p>여러 형태의 config 클래스를 구성하는 부분이 어렵진 않았으나, 많은 시간을 들여 세팅하는 부분과 오류를 수정하는 시간이 많이 걸렸다. 그에 맞게 RabbitMQ의 pub/sub등의 구성과 분리형 클래스 전략에 세심한 신경을 쓰는게 번거로운 부분이었다. 더하여 데이터의 완전한 모니터링 기능을 잘 활용하지 못한 부분의 아쉬움도 있었다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[PM tech]업무 자동 할당 및 업무 완수 예측 서비스 회고록]]></title>
            <link>https://velog.io/@path_creator/PM-tech%EC%97%85%EB%AC%B4-%EC%9E%90%EB%8F%99-%ED%95%A0%EB%8B%B9-%EB%B0%8F-%EC%97%85%EB%AC%B4-%EC%99%84%EC%88%98-%EC%98%88%EC%B8%A1-%EC%84%9C%EB%B9%84%EC%8A%A4-%ED%9A%8C%EA%B3%A0%EB%A1%9D</link>
            <guid>https://velog.io/@path_creator/PM-tech%EC%97%85%EB%AC%B4-%EC%9E%90%EB%8F%99-%ED%95%A0%EB%8B%B9-%EB%B0%8F-%EC%97%85%EB%AC%B4-%EC%99%84%EC%88%98-%EC%98%88%EC%B8%A1-%EC%84%9C%EB%B9%84%EC%8A%A4-%ED%9A%8C%EA%B3%A0%EB%A1%9D</guid>
            <pubDate>Tue, 04 Mar 2025 07:17:53 GMT</pubDate>
            <description><![CDATA[<h3 id="서비스-목표-"><strong>서비스 목표 :</strong></h3>
<p>업무, 업무 일지 및 기록, 멤버등을 수치화하여 스케쥴을 <strong>자동으로 할당하고 분석 및 예측</strong>하는 기능 </p>
<ul>
<li>업무 일지(work stream)를 읽고 그에 타당한 <strong>작업들(tasks)로 요약</strong></li>
<li>작업에 최적화된 <strong>작업자들(team member)을 자동 할당</strong></li>
<li><strong>최단 완수 기간 예측</strong></li>
<li>작업자들의 <strong>완수 퍼포먼스 주기적으로 축정</strong></li>
</ul>
<h3 id="1-서비스를-처음-구상하게-된-계기">1. 서비스를 처음 구상하게 된 계기</h3>
<p><strong>사회적 분위기</strong>
한때 집단 지성과 복잡한 집단 데이터를 분류하고 컨트롤하여 여러 방벙과 솔루션등을 제시하는 컨셉이 트렌드였다. AI와 IT 산업의 발전으로 요즘은 그런 부분을 통제하고 추적하고 컨트롤할 수 있는 기능들이 계속해서 나오고 있다. 
예) SaaS의 프로젝트 협업 툴, HR 도구, 소셜 네트워크 추천 기능 등등</p>
<p><img src=https://velog.velcdn.com/images/path_creator/post/5cf7f20d-7bd3-496a-8182-b02e0d82c8fe/image.png
 width=350 height=350> </p>
<p><strong>계기</strong>
나 역시 그런 부분에 특화된 기술과 기능들에 관심이 있었다. 그런 부분을 통제하고 추적하고 컨트롤할 수 있는 기능을 만들면 얼마나 획기적일까 하고 생각하다가 이번에 인력 지원 작업 기능을 만들었다. 특히 HR이나 사람등을 분석한 경험이 있는 나에게는 탁월한 선택이었다. </p>
<h3 id="2-구상-방법">2. 구상 방법**</h3>
<p><strong>해결의 목적 -</strong> 집단의 작업 능력을 읽고 작업자의 정보를 최대한 활용하여 최단기간에 작업을 마무리할 수 있는 작업 스케쥴 자동 배치 
<strong>어떻게 해결 했는가? -</strong> 알고리즘을 구상하는데 있어서는 많은 어려움이 있었다. </p>
<ul>
<li><strong>어려움 1</strong> 특정 3개의 작업에 다수를 한번에 배치하여 최단 기간의 작업 시간을 만드는 것</li>
<li><strong>어려움 2</strong> 작업 일지를 분석하여 특정 작업 키워드들을 추천하는 방법</li>
<li><strong>어려움 3</strong> 작업들 간의 선후 관계 구성 *<em>예 : *</em> 프로젝트 기획 -&gt; 시장 조사처럼 프로젝트 기획이 있어야 시장 조사가 가능하기 때문에 그 관계 구성에 위배 되지 않으면서 작업을 할당하는 부분</li>
</ul>
<p>3가지 정도의 어려운 부분 이었다. 하지만, 응용 수학과이며 전업 데이터분석인 내 전공을 살려서 획기적인 해결법들을 세가지를 찾았다. 
<img src=https://velog.velcdn.com/images/path_creator/post/85a06ade-f404-4f07-9633-d56ac7572ff8/image.png
 width=300 height=300> </p>
<h4 id="어려움-1의-해결법">어려움 1의 해결법</h4>
<p>NLP 알고리즘과 검색어 추천 알고리즘중에 참조하였다. </p>
<ul>
<li><p><strong>첫번째</strong>로 <strong>코사인 유사도</strong> 텍스트의 특성을 벡터화 시키 후 얼마나 유사도 차이가 큰지 작은지를 비교하여 유사한 단어를 유추하는 분석이다.</p>
</li>
<li><p><strong>두번째</strong>로 <strong>Trie 알고리즘</strong> 텍스트를 파싱하여 tree구조로 만들어 단어를 탐색하고 유사한 단어들을 추천 뽑아내는 알고리즘</p>
</li>
</ul>
<h4 id="어려움-2의-해결법">어려움 2의 해결법</h4>
<ul>
<li><strong>첫번째</strong>로 <strong>그리디 알고리즘</strong> 이부분은 누구나 생각할 수 있는 아이디어이다. 아시겠지만 작업에 참여 가능한 인원들중 가장 작업능력이 월등한 사람들을 순서대로 배치하는 알고리즘 구조를 가지고 있다. </li>
<li><strong>두번째</strong>로 <strong>혼합정수계획법(MILP)</strong> 이부분은 데이터 분석 경력이 있어서 활용할 수 있는 방법이었다. 특정 수치로 작업을 이행하는 다수의 작업자들을 최소한이나 최대한의 시간으로 작업을 끝내는 작업 배치법이다.</li>
<li><strong>세번째</strong>로 <strong>헝가리안 알고리즘</strong> 이부분은 최적화된 작업자들을 찾는 알고리즘이다. 허나 수치의 차이가 없는 데이터를 비교해서 데이터를 할당하는 상황에서 약간의 결함이 발생할 수 있었다. 더하여 익숙치 않은 알고리즘 방식이라서 적용하기엔 무리가 있었다.</li>
</ul>
<h3 id="3-개발-진행에서-처음-경험한-시도들">3. 개발 진행에서 처음 경험한 시도들?</h3>
<h4 id="fast-api와-spring-boot의-이중-호출">fast api와 spring boot의 이중 호출</h4>
<ul>
<li><strong>도커 활용시에 여러 특이점</strong>은 외부 네트워크 설정과 통일, 더하여 정확한 alias나 명칭활용. 여러형태의 포트 설정과 매핑등이 있었다. 여러 블로그에서 어떻게 활용하였는지 학습해보았고 더하여 GPT등을 활용하여 도커코드에 문제가 있는지 점검 하였다.</li>
<li><strong>fast api 함수와 데이터 교환 방식</strong>
WebClientConfig 설정을 이용하여 주소 설정과 Content-Type을 JSON형태로 설정하여 데이터의 송수신을 원활히 처리하였다. 주고 받을 데이터 타입의 형태등 구체적인 설정을 고려하였다. 아쉬운점이 있다면 완전 비동기처리와 에러처리를 예방하는 코드를 추가하지 못한점이었다.<h4 id="코루틴-활용-및-함수형-프로그래밍-활용">코루틴 활용 및 함수형 프로그래밍 활용</h4>
</li>
<li><strong>코루틴 활용시에 특이점은</strong> 서비스 레이어를 suspend 함수로 통일하여 비동기 흐름을 유지, 병렬 적인 async await 적용의 기점을 판단하는게 헷깔렸다. 더하여 사용하면 데이터 교환 처리가 원활히 안되는 경우를 피해서 활용하는 부분이 어려웠었다.  </li>
<li><strong>함수형 프로그래밍 활용시에 특이점은</strong> forEach(), map(), filter(), flatMap() 등의 함수를 활용으로 불변 데이터를 유지했으며 보다 가독성있게 코드를 짜냈었다. 객체나 데이터의 변형이 복잡하게 일어나는 코드에서 함수형 프로그래밍을 유지하는데 혼란스러운적도 있지만 많이 익숙해지니 완전히 적응해 나갔었다.</li>
</ul>
<h4 id="연속-책임-연쇄-패턴-함수의-리팩토링">연속 책임 연쇄 패턴 함수의 리팩토링</h4>
<ul>
<li><strong>책임 연쇄 패턴은</strong> 연쇄적으로 특정 함수가 함수의 기능에 연달아 의존하여 가독성과 오류를 수정하기가 어려운 편이다. 물론, 이형태는 보안 부분에선 뛰어날 수는 있다. 작업 때는 복잡하고 다양한 연계 형태의 함수가 많아서 오류가 걸릴시에 어디에서 오류가 걸렸는지 찾아내기가 쉽지 않았다. 따라서, 함수의 기능성에 의존하기보다 하나의 함수를 병렬적으로 같은 레벨에서 의존하는 방법(퍼사드 패턴식)으로 리팩토링하여 가독성과 오류 수정 부분에서 이점을 얻었다. </li>
<li>*<em>책임 연쇄 패턴의 해결법은 *</em>가능한 하나의 함수를 병렬적으로 같은 레벨에서 의존하는 방법(퍼사드 패턴식)이나, 특정 데이터 값을 갖는 함수로 바꾸어 연쇄를 끊는 것이 미래에 리팩토링 및 업데이트하기 훨씬 수월하다. 추가 적으로 Observer 패턴을 활용하여 이벤트 기반 구조로 리팩토링해도 독립적인 함수 관리가 되기 때문에 좋다.</li>
</ul>
<h3 id="4-그-외의-어려움">4. 그 외의 어려움?</h3>
<h4 id="의존성-개선과-solid-방식">의존성 개선과 SOLID 방식</h4>
<p>몇몇 클래스에선 의존성이 늘어나는 여러 상황들이 있었다. 그때 클래스를 더 분할해서 활용하는 방식이나 퍼사드 패턴을 적극활용하거나 객체를 파라미터에서 받는등의 기법을 활용함으로써 SOLID를 지켜나가려고 했다. <strong>느낀점으로는</strong> 다음엔 좀더 전략적이고 방대한 JPA 인터페이스 구성과 DI 방식으로 최대한 서비스 코드의 의존성을 줄여야 겠다는 생각을 한다. </p>
<h4 id="분석이나-최적화와-관련된-라이브러리-코드를-구성">분석이나 최적화와 관련된 라이브러리 코드를 구성</h4>
<p>분석이나 최적화와 관련된 코드를 다루면서 다소 동떨어진 결과가 나오는 것을 고치기 위해 여러 입증 시도를 하였음. 데이터 형태를 변환하는 다양한 전처리 방식들을 겪으면서 여러 상황의 변수들을 겪음.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[학습 및 독서 효율적인 정보를 습득하기 위한 필기 및 표기법]]></title>
            <link>https://velog.io/@path_creator/%ED%95%99%EC%8A%B5-%EB%B0%8F-%EB%8F%85%EC%84%9C-%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9D%B8-%EC%A0%95%EB%B3%B4%EB%A5%BC-%EC%8A%B5%EB%93%9D%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%9C-%ED%95%84%EA%B8%B0-%EB%B0%8F-%ED%91%9C%EA%B8%B0%EB%B2%95</link>
            <guid>https://velog.io/@path_creator/%ED%95%99%EC%8A%B5-%EB%B0%8F-%EB%8F%85%EC%84%9C-%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9D%B8-%EC%A0%95%EB%B3%B4%EB%A5%BC-%EC%8A%B5%EB%93%9D%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%9C-%ED%95%84%EA%B8%B0-%EB%B0%8F-%ED%91%9C%EA%B8%B0%EB%B2%95</guid>
            <pubDate>Thu, 17 Oct 2024 05:01:36 GMT</pubDate>
            <description><![CDATA[<p>들어가기에 앞서 현재 어떤것들이 있는지 살펴보자</p>
<h2 id="필기법">필기법</h2>
<h3 id="1-코넬식-필기법">1. 코넬식 필기법</h3>
<p><strong>방법</strong></p>
<p>노트를 세 개의 영역으로 나눕니다: 키워드 영역, 노트 필기 영역, 요약 영역.</p>
<p>수업 내용을 키워드와 주요 사항을 중심으로 필기합니다.</p>
<p>수업이 끝난 후, 키워드 영역에 주요 키워드를 작성하고, 요약 영역에 전체 내용을 간단하게 요약합니다.</p>
<p><strong><em>예시</em></strong>
<img src="https://velog.velcdn.com/images/path_creator/post/06393340-c12e-4ff8-8712-18610935f962/image.png" alt=""></p>
<h3 id="2-마인드맵">2. 마인드맵</h3>
<p><strong>방법</strong></p>
<p>종이 중앙에 주제를 적습니다.</p>
<p>주제와 연결되는 하위 주제를 가지처럼 퍼뜨려 적습니다.</p>
<p>관련 정보를 각 가지에 추가하고, 색깔이나 이미지를 사용하여 시각적으로 강조합니다.</p>
<h3 id="3-불렛-저널링">3. 불렛 저널링</h3>
<p><strong>방법</strong></p>
<p>날짜를 적고 그 날의 해야 할 작업을 기호와 함께 정리합니다.</p>
<p>완료된 작업은 체크 표기로 완료 표시를 합니다.</p>
<p>매월 또는 매주 일정을 다시 검토하고 필요 사항을 업데이트합니다.</p>
<p><strong><em>예시</em></strong></p>
<p>10/17/2024</p>
<p>1.</p>
<ul>
<li><input disabled="" type="checkbox"> 과제 제출<ul>
<li>소제목과 내용 </li>
</ul>
</li>
</ul>
<p>2.</p>
<ul>
<li><input checked="" disabled="" type="checkbox"> 독서 30분<ul>
<li>소제목과 내용</li>
</ul>
</li>
</ul>
<p>3.</p>
<ul>
<li><input disabled="" type="checkbox"> 운동하기<ul>
<li>소제목과 내용</li>
</ul>
</li>
</ul>
<h3 id="4-다빈치-노트-필기법">4. 다빈치 노트 필기법</h3>
<p><strong>방법</strong></p>
<p>노트의 왼편에 제목과 주제를 적습니다.</p>
<p>중앙에는 주요 아이디어와 내용을 적고, 오른편에는 추가적인 주석과 질문을 적습니다.</p>
<p><strong><em>예시</em></strong></p>
<p><img src="https://velog.velcdn.com/images/path_creator/post/47c9c32c-5aab-448c-a67d-171b703ed1ba/image.png" alt=""></p>
<h3 id="5-키워드-중심-필기법">5. 키워드 중심 필기법</h3>
<p><strong>방법</strong></p>
<p>수업 중 핵심 단어와 중요 개념을 중심으로 간략하게 필기합니다.</p>
<p>자세한 설명은 필요한 경우 따로 정리합니다.</p>
<p><strong><em>예시</em></strong></p>
<ul>
<li><p>코넬식 필기법</p>
<ul>
<li><p>정의: 정보 체계적 정리</p>
</li>
<li><p>장점: 효율적 복습</p>
</li>
<li><p>단점: 시간 소모</p>
</li>
</ul>
</li>
</ul>
<h3 id="6-색칠-및-강조">6. 색칠 및 강조</h3>
<p><strong>방법</strong></p>
<p>필기 내용을 작성할 때 중요한 정보는 펜이나 형광펜으로 강조합니다.</p>
<p>색깔을 기준으로 다른 범주 또는 주제를 나타내도록 구분합니다.</p>
<p><strong><em>예시</em></strong>
<img src="https://velog.velcdn.com/images/path_creator/post/9d5a194a-aa39-48fd-8672-d4dc42704c30/image.png" alt=""></p>
<p>중요한 정의는 (노란색 또는 빨간색) 하이라이너로 강조.</p>
<p>예시나 사례는 파란색으로 추가.</p>
<p>개념 간의 연결은 연두색 사용.</p>
<p><strong>개인 기호에 따라 변경 가능</strong>
중요 개념이나 정의 : [ ] 자주 쓰임
개념안에서 두번째로 중요한 개념이나 정의 : ( ) 
개념안에서 세번째로 중요한 개념이나 정의 : { }
긍정적인 개념 : / <br>부정적인 개념 : \ /
반대 개념 역접등의 표시 : 개념...설명 &lt;반대 개념&gt;
반대 개념에서 중요한 정의 : 개념...설명 &lt;반대 개념&gt; (&lt;반대 소개념&gt;)
빈도수가 높은걸로 보이는 개념은 별표 : * 개념, 별 갯수로 중요도 표시
암기가 필요한걸로 보이는 것 : @ 내용
자주 실수하는 부분 : % 내용
추가적인 표기법 : -, =, +, $, ;, !, ? O, </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[객체 지향 설계의 원칙 + 전략에 대한 이해]]></title>
            <link>https://velog.io/@path_creator/%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%EC%84%A4%EA%B3%84%EC%9D%98-%EC%9B%90%EC%B9%99-%EC%9B%90%EB%A6%AC-%EC%A0%84%EB%9E%B5%EC%97%90-%EB%8C%80%ED%95%9C-%EC%9D%B4%ED%95%B4</link>
            <guid>https://velog.io/@path_creator/%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%EC%84%A4%EA%B3%84%EC%9D%98-%EC%9B%90%EC%B9%99-%EC%9B%90%EB%A6%AC-%EC%A0%84%EB%9E%B5%EC%97%90-%EB%8C%80%ED%95%9C-%EC%9D%B4%ED%95%B4</guid>
            <pubDate>Thu, 22 Aug 2024 11:00:32 GMT</pubDate>
            <description><![CDATA[<h2 id="보통-좋은-소프트웨어-일수록-모듈의-독립성이-높다고-한다">보통 좋은 소프트웨어 일수록 모듈의 독립성이 높다고 한다.</h2>
<p><strong>독립성이 높다는건? :</strong> 응집도(Cohension)는 강할수록, 결합도(Coupling)는 느슨할 수록 독립성이 높은걸 의미한다.</p>
<p><img src="https://velog.velcdn.com/images/path_creator/post/1eecaef0-2dd5-4cfb-b5de-f083b17bc6ca/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/path_creator/post/43cf15f4-b8f5-463a-ac0d-2a6c9644145f/image.png" alt=""></p>
<ul>
<li><p>결합도(Coupling)가 높은 클래스의 문제점은 클래스의 규모가 커지기 때문에 이해하기 쉽지 않으며, 특정, 객체의 기능 변화에 따른 다른 요소들에 영향을 주기 때문에, 예측이 어려움.</p>
</li>
<li><p><em>결합도가 높다는 건? :*</em> 전역 변수라던지 특정, 객체의 기능을 자주 활용하고 의지한다는 말로써, 그 객체가 변한다면 다른 기능에 많은 영향을 줄 수 있다.</p>
</li>
<li><p>응집도(Cohension) 낮은 클래스의 문제점은 코드를 이해하기가 힘들고, 재사용이 힘듦. 또한 유지보수가 매우 쉽지않으며 클래스 변화에 민감함.</p>
</li>
<li><p><em>응집도가 높다는 건? :*</em> 객체의 기능이 하나의 컨셉으로 명확히 표현되고 다른 객체의 의존을 가능한 하지 않거나 의존을 제공하지 않는 상태에서 한 컨셉 목적의 기능들만을 활용하는 것이다.</p>
</li>
</ul>
<p>따라서 높은 응집도와 낮은 결합도가 이상적인 소프트웨어 모듈이게 된다.</p>
<h2 id="객체지향의-개념과-solid-설계-적용">객체지향의 개념과 SOLID 설계 적용</h2>
<p><strong>SOLID</strong>
<strong>S</strong>RP (Single Responsibility Principle 단일책임의 원칙)
<strong>O</strong>CP(Open Closed Principle 개방패쇄의 원칙)
<strong>L</strong>SP(Liskov Substitution Principle 리스코프 치환의 원칙)
<strong>I</strong>SP(Interface Segregation Principle 인터페이스 분리의 원칙)
<strong>D</strong>IP(Dependency Inversion Principle 의존성 역전의 원칙)</p>
<p><strong>SOLID외에 OOP에서 지켜야할 추가적인 규칙 :</strong> 의미에 맞는 이름 명칭 사용, 함수안에 매개변수(파라미터) 최소화, 전역적인 형태의 함수 최소화, static 형태의 함수 최소화, 생성자 최소화, 조건문 최소화   </p>
<h3 id="srp-single-responsibility-principle"><strong>S</strong>RP (Single Responsibility Principle)</h3>
<blockquote>
<p><strong>S</strong>RP (Single Responsibility Principle 단일책임의 원칙) : 
 하나의 클래스는 하나의 기능 담당하여 하나의 책임을 수행하는데 집중되도록 클래스를 여러개 설계하라는 원칙</p>
</blockquote>
<ul>
<li>이 개념은 단순한 개념을 기반으로 클래스를 구성하여 결합도를 줄이는 원칙</li>
<li><em>Tip :*</em> 한 클래스의 개념을 단 한문장으로 표현해야함.
<strong>나쁜 예)</strong> 작업을 자동 배분하고, 최단 기간의 작업 결과를 할당한다.
<strong>좋은 예)</strong> 작업을 배분한 경우의 수를 활용해 최단 기간의 작업 결과를 할당한다.</li>
</ul>
<p><strong>최대한 객체를 쪼개라</strong>
<img src="https://velog.velcdn.com/images/path_creator/post/7da563cd-93d4-4e0c-aba8-6f63d8fc9633/image.png" alt=""></p>
<h3 id="ocpopen-closed-principle"><strong>O</strong>CP(Open Closed Principle)</h3>
<blockquote>
<p><strong>O</strong>CP(Open Closed Principle 개방패쇄의 원칙) :
 소프트웨어의 구성요소(컴포넌트,클래스,모듈,함수)는 확장에는 열려있고, 변경에는 닫혀있어야 하는 원칙</p>
</blockquote>
<ul>
<li>기존 구성요소는 수정이 일어나지 말아야 하며, 기존 구성요소를 쉽게 확장(예: 상속, 인터페이스 활용)해서 재사용할 수 있어야 한다는 뜻
클래스가 많아져서 코드관리가 어려워 질 수 있고, 결합도 역시 올라갈 가능성이 존재
<strong>TIP :</strong> 확장될 것과 변하지 않을 것을 엄격히 구분하고, 부모 클래스는 추상적이게 함수를 표현 + 인터페이스에 가능한한 의존</li>
</ul>
<p><strong>다른 객체가 다른 객체를 참조시에는 인터페이스||상위 클래스를 참조하여 객체의 변경/업데이트를 원활히 함</strong>
<img src="https://velog.velcdn.com/images/path_creator/post/e8877ff4-1555-444c-9330-d437d3a6febd/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/path_creator/post/16e40430-1133-4a9e-9413-a444f7818ce8/image.png" alt=""></p>
<p>참조
<img src="https://velog.velcdn.com/images/path_creator/post/a5cb0d1c-8897-4603-9348-1940eaf0b77f/image.png" alt=""></p>
<h3 id="lspliskov-substitution-principle"><strong>L</strong>SP(Liskov Substitution Principle)</h3>
<blockquote>
<p><strong>L</strong>SP(Liskov Substitution Principle 리스코프 치환의 원칙) :
 자식 클래스는 부모 클래스에서 가능한 행위를 수행해야 하며, 객제 지향 프로그래밍에서는 부모 클래스의 인스턴스 대신 자식 클래스의 인스턴스를 사용해도 문제가 없다는 것</p>
</blockquote>
<ul>
<li>개발의 유연성과 복잡성의 문제가 생길 수 있지만 유지 보수 및 코드 재사용성이 증가
<strong>TIP :</strong> 부모 클래스와 자식 클래스 사이에는 일관된 함수가 존재해야하고, 추가적인 방법으론 공통된 인터페이스를 중심으로 둘이 다른 기능을 만드는 방법으로 구성  </li>
</ul>
<p><strong>하위 클래스는 상위 클래스들의 모든 기능들이 가능해야 함</strong> 
<img src="https://velog.velcdn.com/images/path_creator/post/e4ad96b2-7f7e-4334-ad25-878c01af33f3/image.png" alt=""></p>
<h3 id="ispinterface-segregation-principle"><strong>I</strong>SP(Interface Segregation Principle)</h3>
<blockquote>
<p><strong>I</strong>SP(Interface Segregation Principle 인터페이스 분리의 원칙) :
 어떤 클래스가 다른 클래스에 종속될 때에는 가능한 최소한의 인터페이스만을 사용해야 함 </p>
</blockquote>
<ul>
<li><p>하나의 일반적인 인터페이스보다는 여러 개의 구체적인 인터페이스가 낫다 라고 정의 </p>
</li>
<li><p><em>TIP :*</em> 인터페이스를 작고 명확하게 분리하고 정의하여 낮은 결합도를 구성</p>
</li>
<li><p><em>여러 구체적인 하위 인터페이스들을 활용하여, 하위 클래스들의 의존을 가능케함*</em> 
<img src="https://velog.velcdn.com/images/path_creator/post/9f501948-a1e6-4518-b585-accdcb4e59ca/image.png" alt=""></p>
<h3 id="dip-dependency-inversion-principle"><strong>D</strong>IP (Dependency Inversion Principle)</h3>
<blockquote>
<p><strong>D</strong>IP(Dependency Inversion Principle 의존성 역전의 원칙)
이는 의존관계를 맺을 때, 변화하기 쉬운 것보다는 변화하기 어려운 것에 의존해야 한다는 의미이다. 여기서 말하는 변화하기 쉬운 것은 객체지향의 관점에서 보면 구체화 된 클래스를 의미하고, 변화하기 어려운 것이란 추상클래스나 인터페이스를 의미</p>
</blockquote>
<ul>
<li>추상 클래스나 인터페이스에 주로 의존하며 변화하기 어려운 것 거의 변화가 없는 것에 의존</li>
<li><em>TIP :*</em> 추상화된 클래스나 인터페이스를 설계하며 모듈을 분리하여 결합도를 줄이는게 가능</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/path_creator/post/841cc072-e2e0-4400-ac54-7634fddf8930/image.png" alt=""></p>
<p>*<em>참조 : *</em>
<a href="https://inpa.tistory.com/entry/OOP-%F0%9F%92%A0-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EA%B2%B0%ED%95%A9%EB%8F%84-%EC%9D%91%EC%A7%91%EB%8F%84-%EC%9D%98%EB%AF%B8%EC%99%80-%EB%8B%A8%EA%B3%84-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-%EC%89%BD%EA%B2%8C-%EC%A0%95%EB%A6%AC">https://inpa.tistory.com/entry/OOP-%F0%9F%92%A0-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EA%B2%B0%ED%95%A9%EB%8F%84-%EC%9D%91%EC%A7%91%EB%8F%84-%EC%9D%98%EB%AF%B8%EC%99%80-%EB%8B%A8%EA%B3%84-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-%EC%89%BD%EA%B2%8C-%EC%A0%95%EB%A6%AC</a></p>
<p><a href="https://medium.com/@jang.wangsu/%EC%84%A4%EA%B3%84-%EC%9A%A9%EC%96%B4-%EC%9D%91%EC%A7%91%EB%8F%84%EC%99%80-%EA%B2%B0%ED%95%A9%EB%8F%84-b5e2b7b210ff">https://medium.com/@jang.wangsu/%EC%84%A4%EA%B3%84-%EC%9A%A9%EC%96%B4-%EC%9D%91%EC%A7%91%EB%8F%84%EC%99%80-%EA%B2%B0%ED%95%A9%EB%8F%84-b5e2b7b210ff</a></p>
<p><a href="https://inpa.tistory.com/entry/OOP-%F0%9F%92%A0-%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%EC%84%A4%EA%B3%84%EC%9D%98-5%EA%B0%80%EC%A7%80-%EC%9B%90%EC%B9%99-SOLID">https://inpa.tistory.com/entry/OOP-%F0%9F%92%A0-%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%EC%84%A4%EA%B3%84%EC%9D%98-5%EA%B0%80%EC%A7%80-%EC%9B%90%EC%B9%99-SOLID</a></p>
<p><a href="https://velog.io/@juhwan9408/%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%EC%84%A4%EA%B3%84-5%EC%9B%90%EC%B9%99-SOLID">https://velog.io/@juhwan9408/%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%EC%84%A4%EA%B3%84-5%EC%9B%90%EC%B9%99-SOLID</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백엔드에서 자주 사용되는  디자인패턴 -생성 패턴형-]]></title>
            <link>https://velog.io/@path_creator/%EB%B0%B1%EC%97%94%EB%93%9C%EC%97%90%EC%84%9C-%EC%9E%90%EC%A3%BC-%EC%82%AC%EC%9A%A9%EB%90%98%EB%8A%94-%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4</link>
            <guid>https://velog.io/@path_creator/%EB%B0%B1%EC%97%94%EB%93%9C%EC%97%90%EC%84%9C-%EC%9E%90%EC%A3%BC-%EC%82%AC%EC%9A%A9%EB%90%98%EB%8A%94-%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4</guid>
            <pubDate>Tue, 13 Aug 2024 12:20:11 GMT</pubDate>
            <description><![CDATA[<h3 id="span-stylebackground-color-rgba1020050005----생성-패턴-----span"><span style="background-color: rgba(10,200,500,0.5)">--- <em>생성 패턴</em> ----</span></h3>
<h4 id="깃허브-링크--httpsgithubcomjinhan-han-jeremyrealdesignpattern">깃허브 링크 : <a href="https://github.com/Jinhan-Han-Jeremy/RealDesignPattern">https://github.com/Jinhan-Han-Jeremy/RealDesignPattern</a></h4>
<h3 id="span-stylebackground-color-rgba1020010005----------팩토리-메서드factory-method---------span"><span style="background-color: rgba(10,200,100,0.5)"> -------- 팩토리 메서드(Factory Method) --------</span></h3>
<p>객체 생성을 서브 클래스나 메서드로 분리해서 위임하는 패턴</p>
<ul>
<li><strong>팩토리 메서드 패턴을 사용하는 이유</strong></li>
<li><ul>
<li>클래스의 생성과 사용 처리 로직을 분리하여 결합도를 낮추기 위함 (유지보수 up).</li>
</ul>
</li>
<li><ul>
<li>객체 생성 처리를 서브클래스로 분리 함으로써, 다양한 객체 생성 가능 (확장성 up). </li>
</ul>
</li>
<li><ul>
<li>객체 생성 로직을 캡슐화하여 여러 곳에서 재사용 가능 (일관성 유지).</li>
</ul>
</li>
</ul>
<p><strong>자주 사용되는 경우</strong> : 데이터베이스 연결 객체 생성, HTTP 클라이언트 생성, 파서(Parser) 객체 생성, 알림(Notification) 서비스 객체 생성, 보고서(Report) 객체 생성, 파일 처리기(File Handler) 객체 생성, 지불 게이트웨이(Payment Gateway) 객체 생성, 메시지 브로커(Message Broker) 클라이언트 생성</p>
<ul>
<li>유연성 제공, 코드 재사용성 향상, 객체 생성을 관리하고 제어, 확장성 향상, 일관성 유지</li>
</ul>
<p><strong>추상 팩토리(Abstract Factory)</strong>
구체적인 클래스를 지정하지 않고, 상황에 맞는 객체를 생성하기 위한 인터페이스를 제공하는 패턴</p>
<p><strong>추상 팩토리 매서드 vs 팩토리 매서드</strong></p>
<p><img src="https://velog.velcdn.com/images/path_creator/post/537b02cc-5859-461d-957d-e1a13a2735f2/image.png" alt=""></p>
<ul>
<li><strong>추상 팩토리 매서드</strong></li>
<li><em>장점*</em> - 관련 객체들을 일관된 방식으로 생성, 기존 코드를 수정하지 않고 객체 생성, 코드 재사용성</li>
<li><em>단점*</em> - 인터페이스 증가, 객체 생성 로직의 복잡성 증가</li>
<li><strong>팩토리 매서드</strong></li>
<li><em>장점*</em> - 객체 생성 방식을 쉽게 변경 가능, 코드 재사용성</li>
<li><em>단점*</em> - 클래스 증가, 객체 생성 로직이 분산</li>
</ul>
<p><strong>팩토리 매서드 코드</strong></p>
<blockquote>
<p>팩토리 매서드 예제) <strong>프로덕트의 오브젝트와 인터페이스 구성</strong>
1.** public interface FactoryProduct { :** 객체의 함수 기능을 하는 인터페이스 선언 </p>
</blockquote>
<pre><code class="language-java">package FactoryMethodProduct;
public interface FactoryProduct {
    void use();
}</code></pre>
<p>2.** public class ConcreteProductA implements FactoryProduct { :** 인터페이스를 활용한 오브젝트 구성 - 하나의 인터페이스를 재사용 </p>
<pre><code class="language-java">// ConcreteProductA 클래스 - Product 인터페이스의 구현체
package FactoryMethodProduct;
public class ConcreteProductA implements FactoryProduct {
    @Override
    public void use() {
        System.out.println(&quot;Using Product A&quot;);
    }
}
// ConcreteProductB 클래스 - Product 인터페이스의 또 다른 구현체
package FactoryMethodProduct;
public class ConcreteProductB implements FactoryProduct {
    @Override
    public void use() {
        System.out.println(&quot;Using Product B&quot;);
    }
}```

&gt;팩토리 매서드 예제) **추상 클래스를 이용하여 오브젝트 생성**
3. **public abstract class FactoryCreator { :** 추상 클래스를 선언.
4. **public abstract FactoryProduct factoryMethod(); :** 인터페이스를 추상 형태로 선언 및 활용.
```java
public abstract class FactoryCreator {
    // 팩토리 메서드 - 구체적인 객체 생성을 위한 추상 메서드
    public abstract FactoryProduct factoryMethod();
// 클라이언트 메서드 - 팩토리 메서드를 사용하여 객체를 생성하고 사용하는 메서드
    public void doSomething() {
        FactoryProduct product = factoryMethod();
        product.use();
    }
}</code></pre>
<ol start="5">
<li><strong>public class ConcreteCreatorA extends FactoryCreator { :</strong> 특정 객체를 만들기 위해 추상 객체 상속.</li>
<li><strong>@Override public FactoryProduct factoryMethod() { :</strong> 인터페이스를 이용하여 특정 객체의 함수 상속.  <pre><code class="language-java">package FactoryMethodProduct;
public class ConcreteCreatorA extends FactoryCreator {
 @Override
 public FactoryProduct factoryMethod() {
     return new ConcreteProductA();
 }
}
// ConcreteCreatorB 클래스 - Creator 클래스를 확장하여 ProductB 객체를 생성하는 클래스
package FactoryMethodProduct;
public class ConcreteCreatorB extends FactoryCreator {
 @Override
 public FactoryProduct factoryMethod() {
     return new ConcreteProductB();
 }
}</code></pre>
<blockquote>
<p>팩토리 매서드 예제) <strong>메인에서 클라이언트 구성</strong></p>
</blockquote>
<pre><code class="language-java">// MainByFactory 클래스 - 팩토리 메서드 패턴을 사용하는 클라이언트 코드
import FactoryMethodProduct.FactoryCreator;
import FactoryMethodProduct.ConcreteCreatorA;
import FactoryMethodProduct.ConcreteCreatorB;
public class MainByFactory {
 public static void main(String[] args) {
     // ConcreteCreatorA를 사용하여 ProductA 객체 생성
     FactoryCreator creatorA = new ConcreteCreatorA();
     creatorA.doSomething();
     // ConcreteCreatorB를 사용하여 ProductB 객체 생성
     FactoryCreator creatorB = new ConcreteCreatorB();
     creatorB.doSomething();
 }
}</code></pre>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/path_creator/post/81c0da40-3741-4766-a451-ac98f68fe227/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/path_creator/post/738e1010-598c-400a-8347-4062cd66c3e5/image.png" alt=""></p>
<p>*<em>전체적 구조 : *</em></p>
<ul>
<li>기본 팩토리 클래스 { 추상 객체 생성 함수, 객체를 생성하고 사용하는 메서드}</li>
<li>하위 팩토리 implements 기본 팩토리 클래스{@Override객체(서비스 기능 담당) 생성 기능}</li>
<li>기본 서비스 인터페이스{서비스 함수}</li>
<li>기본 서비스 클래스 implements 인터페이스{@Override서비스 함수}  <br/>

</li>
</ul>
<h3 id="span-stylebackground-color-rgba1020010005----------추상-팩토리-매서드-코드---------span"><span style="background-color: rgba(10,200,100,0.5)"> -------- 추상 팩토리 매서드 코드 --------</span></h3>
<blockquote>
<p>추상 팩토리 매서드 예제) <strong>프로덕트의 오브젝트와 인터페이스 구성</strong></p>
</blockquote>
<ol>
<li><strong>public interface AbstractFactory { :</strong> 인터페이스들을 관리하는 인터페이스 생성</li>
<li><strong>AbstractFactoryProduct createProductA(); :</strong> 인터페이스 기반으로 서비스 객체 생성 기능을 하는 함수를 콜 <pre><code class="language-java">package AbstractFactoryMethodProduct;
public interface AbstractFactory {
 AbstractFactoryProduct createProductA();
 AbstractFactoryProduct createProductB();
}```
</code></pre>
</li>
</ol>
<blockquote>
</blockquote>
<ol start="3">
<li><strong>public class ConcreteFactory1 implements AbstractFactory{ :</strong> 팩토리 오브젝트로 두가지(1,2) 유형이 존재하며 인터페이스를 활용하여 객체 생성</li>
<li><strong>@Override public AbstractFactoryProduct createProductA() { :</strong> 두가지(A,B) 유형의 객체 생성을 하는 클래스<pre><code class="language-java">package AbstractFactoryMethodProduct;
public class ConcreteFactory1 implements AbstractFactory{
 @Override
 public AbstractFactoryProduct createProductA() {
     return new ConcreteProductA1();
 }
 @Override
 public AbstractFactoryProduct createProductB() {
     return new ConcreteProductB1();
 }
}
package AbstractFactoryMethodProduct;
public class ConcreteFactory2 implements AbstractFactory {
 @Override
 public AbstractFactoryProduct createProductA() {
     return new ConcreteProductA2();
 }
 @Override
 public AbstractFactoryProduct createProductB() {
     return new ConcreteProductB2();
 }
}</code></pre>
</li>
</ol>
<blockquote>
<p>추상 팩토리 매서드 예제) <strong>클라이언트에서 기능을 하는 오브젝트 구현</strong>
5. <strong>public interface AbstractFactoryProduct { :</strong> 핵심 객체의 인터페이스 구현
6. <strong>public class ConcreteProductA1 implements AbstractFactoryProduct {
@Override public void use() { :</strong> 핵심 객체로 인터페이스 참조 및 핵심 메소드 override</p>
</blockquote>
<pre><code class="language-java">package AbstractFactoryMethodProduct;
public interface AbstractFactoryProduct {
    void use();
}
package AbstractFactoryMethodProduct;
public class ConcreteProductA1 implements AbstractFactoryProduct {
    @Override
    public void use() {
        System.out.println(&quot;Using Product A1&quot;);
    }
}
package AbstractFactoryMethodProduct;
public class ConcreteProductA2 implements AbstractFactoryProduct {
    @Override
    public void use() {
        System.out.println(&quot;Using Product A2&quot;);
    }
}</code></pre>
<blockquote>
</blockquote>
<ol start="7">
<li><strong>AbstractFactory factory1 = new ConcreteFactory1( );</strong> : 인터페이스 기반에 객체 생성 그리고 그 인터페이스를 받아 이용했던 ConcreteFactory1 클래스 할당</li>
<li><strong>AbstractFactoryProduct productA1 = factory1.createProductA(); :</strong> 서비스를 위한 인터페이스 객체에 서비스를 위한 핵심 프로덕트를 구현하는 메소드 할당</li>
<li><strong>productA1.use(); :</strong> 서비스 기능을 하는 핵심 객체의 함수 콜<pre><code class="language-java">import AbstractFactoryMethodProduct.*;
public class MainByAbstractFactory {
 public static void main(String[] args) {
     // ConcreteFactory1을 사용하여 관련 객체들 생성
     AbstractFactory factory1 = new ConcreteFactory1();
     AbstractFactoryProduct productA1 = factory1.createProductA();
     AbstractFactoryProduct productB1 = factory1.createProductB();
     productA1.use();
     productB1.use();
     // ConcreteFactory2를 사용하여 관련 객체들 생성
     AbstractFactory factory2 = new ConcreteFactory2();
     AbstractFactoryProduct productA2 = factory2.createProductA();
     AbstractFactoryProduct productB2 = factory2.createProductB();
     productA2.use();
     productB2.use();
 }
}</code></pre>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/path_creator/post/7f63f500-aad7-4c8b-ad61-efb2148dae2a/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/path_creator/post/2ef240a1-b47c-4a81-b095-81b38b8fa4d6/image.png" alt=""></p>
<p>*<em>전체적 구조 : *</em></p>
<ul>
<li>기본 팩토리 인터페이스{객체(서비스 기능 담당) 생성 기능들의 집합}</li>
<li>기본 팩토리 클래스 implements 인터페이스{ 클래스 특정 객체(서비스 기능 담당)를 생성 }</li>
<li>기본 서비스 인터페이스{서비스 함수}</li>
<li>기본 서비스 클래스 implements 인터페이스{@Override서비스 함수}  </li>
</ul>
<h3 id="span-stylebackground-color-rgba1020010005---------싱글톤singleton---------span"><span style="background-color: rgba(10,200,100,0.5)">-------- 싱글톤(Singleton) --------</span></h3>
<p>한 클래스마다 인스턴스를 하나만 생성해서 인스턴스가 하나임을 보장하고 어느 곳에서도 접근할 수 있게 제공하는 패턴</p>
<ul>
<li><strong>싱글톤 패턴을 사용하는 이유</strong></li>
<li><ul>
<li>객체를 생성할 때마다 메모리 영역을 할당받지 않아서, 메모리낭비를 방지 가능. </li>
</ul>
</li>
<li><ul>
<li>싱글톤으로 구현한 인스턴스는 &#39;전역&#39; 이므로, 다른 클래스의 인스턴스들이 데이터를 공유 가능.</li>
</ul>
</li>
</ul>
<p><strong>자주 사용되는 경우</strong> : 설정 관리 (Configuration Management), 로깅 (Logging), 캐시 관리 (Cache Management), 데이터베이스 연결 풀 (Database Connection Pool), 스레드 풀 (Thread Pool), 디바이스 설정 (Device Configuration), 외부 서비스 연결 (External Service Connection), 플러그인 관리 (Plugin Management), 세션 관리 (Session Management), 타이머 및 스케줄러 (Timer and Scheduler), 보안 설정 (Security Configuration)</p>
<ul>
<li>메모리 효율성, 전역 접근성, 인스턴스 제어, 자원 관리, 일관성 유지</li>
</ul>
<p><strong>싱글톤의 문제 해결 - statelsess</strong></p>
<ul>
<li>싱글톤의 단점을 해결하기 위해 무상태(stateless)로 설계해야 한다.</li>
<li><ul>
<li>특정 클라이언트에 의존적인 필드가 있으면 안 된다.</li>
</ul>
</li>
<li><ul>
<li>특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안 된다.</li>
</ul>
</li>
</ul>
<blockquote>
<p>싱글톤 예제) <strong>로그를 기록하고 저장하는 역할</strong>
1.** private static SingletonLogger instance; :** private static으로 클래스 레벨에서 단일 인스턴스를 공유할 수 있도록 하며, 정적 메서드가 이 인스턴스에 접근할 수 있게 함.
2. <strong>private SingletonLogger() :</strong> 외부에서 인스턴스를 생성하지 못하도록 하여 단일 인스턴스 생성을 보장 더하여 특정 상태를 나타내는 변수를 함수를 포함 하지않는다. 
이유는 private static등으로 변수가 관리되기 때문에 인스턴스들이 상태값을 모두 변형시키는 문제를 초래함. </p>
</blockquote>
<pre><code class="language-java">import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
public class SingletonLogger {
    // 1. 유일한 인스턴스를 저장하기 위한 정적 필드
    private static SingletonLogger instance;
    // 로그 파일을 기록할 PrintWriter 객체
    private PrintWriter writer;
    // 2. private 생성자: 외부에서 인스턴스를 생성하지 못하도록 함
    private SingletonLogger() {
        try {
            FileWriter fileWriter = new FileWriter(&quot;app.log&quot;, true);
            writer = new PrintWriter(fileWriter, true);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }</code></pre>
<ol start="3">
<li><strong>public static synchronized SingletonLogger getInstance() :</strong> 여러 스레드에 의해 동시에 인스턴스들을 호출하는 경우를 예방하고, getInstance를 통하여 여러 인스턴스들을 생성 및 관리.<blockquote>
</blockquote>
<pre><code class="language-java">// 3. 인스턴스를 반환하는 정적 메서드
//synchronized로 여러 스레드에 의해 동시에 인스턴스들을 호출하는 경우를 예방
// 싱글톤은 여러 인스턴스가 생성되는 것을 예방
public static synchronized SingletonLogger getInstance() {
    if (instance == null) {
        instance = new SingletonLogger();
    }
    return instance;
}
// 로그 메시지를 기록하는 메서드
public void log(String message) {
    String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern(&quot;yyyy-MM-dd HH:mm:ss&quot;));
    writer.println(timestamp + &quot; - &quot; + message);
}
}</code></pre>
</li>
</ol>
<blockquote>
<p>싱글톤 예제) <strong>인스턴스 활용과 스테이트레스 싱글톤</strong></p>
</blockquote>
<pre><code class="language-java">import java.util.Scanner;
public class Main {
    public static String LevelAccess(int lv)
    {
        String LevelState;
        if(lv &lt;= 10){
            LevelState = &quot;Low level is accessed&quot;;
        }
        else {
            LevelState = &quot;High level is accessed&quot;;
        }
        return LevelState;
    }
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        // 싱글톤 인스턴스를 가져옴
        //아래처럼 부르는건 스테이트레스로 인스턴스의 상태를 변경하지 않으며, 외부로부터 주어진 데이터를 처리하는 역할
        SingletonLogger singletonLogger = SingletonLogger.getInstance();
        // 로그 메시지를 기록
        try {
            // 애플리케이션 시작 로그
            singletonLogger.log(&quot;Application started.&quot;);
            // 첫 번째 질문을 출력하고 사용자로부터 입력을 받음
            System.out.print(&quot;What is your name? &quot;);
            String name = scanner.nextLine();
            singletonLogger.log(name);
            // 두 번째 질문을 출력하고 사용자로부터 입력을 받음
            System.out.print(&quot;What is your lv? &quot;);
            int lv = scanner.nextInt();
            // 버퍼를 비워 다음 입력을 받을 준비를 함
            scanner.nextLine(); // nextInt 후에 남아있는 newline 문자를 제거하기 위해 nextLine() 호출
            // 레벨에 따른 접근 로그 생성
            String accessLog = LevelAccess(lv);
            singletonLogger.log(accessLog);
        } catch (Exception e) {
            // 예외 발생 시 로그에 기록
            singletonLogger.log(&quot;An error occurred: &quot; + e.getMessage());
            e.printStackTrace();
        } finally {
            // Scanner 객체를 닫음
            scanner.close();
            // 애플리케이션 종료 로그
            singletonLogger.log(&quot;Application finished.&quot;);
        }
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/path_creator/post/2f893c3c-eb8e-4997-be2f-bed79c9bb3fd/image.png" alt=""></p>
<p>*<em>전체적 구조  : *</em></p>
<ul>
<li>싱글톤 클래스 { 상태없는 기본 생성자, 단일 인스턴스를 생성하고 사용하는 메서드, 서비스 메소드}</li>
</ul>
<p>참조 :
<a href="https://dev-coco.tistory.com/177">https://dev-coco.tistory.com/177</a></p>
<p><a href="https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EC%B6%94%EC%83%81-%ED%8C%A9%ED%86%A0%EB%A6%ACAbstract-Factory-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90#%EC%B6%94%EC%83%81_%ED%8C%A9%ED%86%A0%EB%A6%AC_%ED%8C%A8%ED%84%B4_%ED%8A%B9%EC%A7%95">https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EC%B6%94%EC%83%81-%ED%8C%A9%ED%86%A0%EB%A6%ACAbstract-Factory-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90#%EC%B6%94%EC%83%81_%ED%8C%A9%ED%86%A0%EB%A6%AC_%ED%8C%A8%ED%84%B4_%ED%8A%B9%EC%A7%95</a></p>
<p><a href="https://innovation123.tistory.com/9#%EC%8B%B1%EA%B8%80%ED%86%A4%EC%9D%98%20%EB%AC%B8%EC%A0%9C%20%ED%95%B4%EA%B2%B0%20-%20statelsess-1">https://innovation123.tistory.com/9#%EC%8B%B1%EA%B8%80%ED%86%A4%EC%9D%98%20%EB%AC%B8%EC%A0%9C%20%ED%95%B4%EA%B2%B0%20-%20statelsess-1</a></p>
<p><a href="https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%ED%85%9C%ED%94%8C%EB%A6%BF-%EB%A9%94%EC%86%8C%EB%93%9CTemplate-Method-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90">https://inpa.tistory.com/entry/GOF-💠-템플릿-메소드Template-Method-패턴-제대로-배워보자</a></p>
<p><a href="https://appleg1226.tistory.com/category/Study?page=2">https://appleg1226.tistory.com/category/Study?page=2</a></p>
<p><a href="https://ssow93.tistory.com/53">https://ssow93.tistory.com/53</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[코딩테스트 위한 Python 기본 문법 정리]]></title>
            <link>https://velog.io/@path_creator/%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%9C%84%ED%95%9C-Python-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@path_creator/%EC%BD%94%EB%94%A9%ED%85%8C%EC%8A%A4%ED%8A%B8-%EC%9C%84%ED%95%9C-Python-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Tue, 13 Aug 2024 12:00:46 GMT</pubDate>
            <description><![CDATA[<h2 id="실수-표현-정확도-한계-03--060899">실수 표현 정확도 한계 0.3 + 0.6=0.899..</h2>
<blockquote>
<pre><code class="language-python">a = 0.3 + 0.6  ## a결과 0.8999
if a == 0.9: 
  print(True)
else:
  print(False)  # 결과 : False</code></pre>
</blockquote>
<pre><code>
**해결 방법 : round(수, 표시 소수 자리 수) 반올림 권장 **
함수(변수) 형태는-&gt; **변수 = 함수(변수)** 형태로 할당해야 변수값 변경됨 
&gt; a = round(0.3 + 0.6, 1)  # 0.9

## str, list, tuple, dict, set

### --- str ---
생성 **= &quot;Hello&quot; - or - &#39;world&#39; - or - str(123)**

인덱싱, 슬라이싱 가능
특정 인덱스 값 변경 불가 (immutable)

** 변환 **
&gt;
**문자열을 리스트로 변환**
list_from_str = list(&quot;Hello&quot;)
&gt;
**문자열을 튜플로 변환**
tuple_from_str = tuple(&quot;Hello&quot;)
&gt;
**문자열을 집합으로 변환 (중복 제거)**
set_from_str = set(&quot;Hello&quot;)

### --- list ---
생성 **= [1, 2, 3] - or - list( [1, 2, 3] )**

- 인덱싱, 슬라이싱 가능 (순서가 있음)
- 원소 중복 가능
- 특정 인덱스 값 변경 가능 (mutable)
- 원소 추가(append, insert) 제거(remove) 가능

**변환**
&gt;
**리스트를 문자열로 변환**
str_from_list = &#39;&#39;.join([&#39;a&#39;, &#39;b&#39;, &#39;c&#39;])
&gt;
**리스트를 튜플로 변환**
tuple_from_list = tuple([1, 2, 3])
&gt;
**리스트를 집합으로 변환**
set_from_list = set([1, 2, 2, 3])
&gt;
**리스트를 dict로 변환**
```python
dict_from_list = {i: list_data[i] for i in range(len(list_data))} 
# {0: &#39;a&#39;, 1: &#39;b&#39;, 2: &#39;c&#39;}</code></pre><h3 id="----tuple----">--- tuple ---</h3>
<p>생성 <strong>= (1, 2, 3) - or - tuple((1, 2, 3))</strong></p>
<ul>
<li>인덱싱, 슬라이싱 가능 (순서가 있음)</li>
<li>원소 중복 가능</li>
<li>특정 인덱스 값(=한번 선언된 값) 변경 불가 (immutable)</li>
<li>원소 추가(append, insert) 제거(remove) 불가</li>
</ul>
<p><strong>변환</strong></p>
<blockquote>
</blockquote>
<p><strong>튜플을 문자열로 변환</strong>
str_from_tuple = &#39;&#39;.join( (&#39;a&#39;, &#39;b&#39;, &#39;c&#39;) )</p>
<blockquote>
</blockquote>
<p><strong>튜플을 리스트로 변환</strong>
list_from_tuple = list( (1, 2, 3) )</p>
<blockquote>
</blockquote>
<p><strong>튜플을 집합으로 변환</strong>
set_from_tuple = set( (1, 2, 2, 3) )</p>
<blockquote>
</blockquote>
<p><strong>튜플을 dict로 변환</strong></p>
<pre><code class="language-python">dict_from_tuple = {i: tuple_data[i] for i in range(len(tuple_data))}
# {0: &#39;a&#39;, 1: &#39;b&#39;, 2: &#39;c&#39;}</code></pre>
<h3 id="----dict----">--- dict ---</h3>
<p>생성 <strong>= {&#39;name&#39;: &#39;Min&#39;, &#39;age&#39;: 12} - or -  dict({&#39;name&#39;: &#39;Min&#39;, &#39;age&#39;: 12}) - or - dict(a=1, b=2, c=3) - or - dict( [ (&#39;a&#39;, 1), (&#39;b&#39;, 2), (&#39;c&#39;, 3) ] )</strong></p>
<ul>
<li>&#39;key&#39; : &#39;value&#39;의 쌍을 데이터로 갖는 자료형</li>
<li>인덱싱 불가 (순서가 없음)</li>
<li>&#39;key&#39;는 중복 불가. 중복 시 뒤에 선언된 값 사용</li>
<li>dict 원소 추가, 삭제 가능</li>
<li>&#39;key&#39;값으로 &#39;value&#39;값 조회, 수정 O(1) → 리스트보다 성능이 좋음</li>
<li>키와 값의 자료형 모두 통일하지 않아도 되고 섞어서 사용 가능</li>
</ul>
<h3 id="----set----">--- set ---</h3>
<p>생성 <strong>= set([1, 2, 2, 3])  - or - {1, 2, 3} - or - s = set() → 빈 중괄호 {}는 dict 클래스가 생성되므로 주의</strong></p>
<ul>
<li>인덱싱 불가 (순서가 없음) → for i in s 로 출력해보면 어떤 값이 먼저 나올지 모름</li>
<li>원소 중복 불가 (key 값)</li>
<li>원소 추가, 제거, 조회 가능</li>
<li>원소로 조회 O(1)</li>
<li>default가 오름차순 정렬</li>
</ul>
<p><strong>변환</strong></p>
<blockquote>
</blockquote>
<p><strong>집합을 문자열로 변환</strong>
str_from_set = &#39;&#39;.join(set([&#39;a&#39;, &#39;b&#39;, &#39;c&#39;]))</p>
<blockquote>
</blockquote>
<p><strong>집합을 리스트로 변환</strong>
list_from_set = list({1, 2, 3})</p>
<blockquote>
</blockquote>
<p><strong>집합을 튜플로 변환</strong>
tuple_from_set = tuple({1, 2, 3})</p>
<h3 id="----list-2개를-key-value로-하여-dictionary-생성----">--- list 2개를 key, value로 하여 dictionary 생성 ---</h3>
<blockquote>
</blockquote>
<pre><code class="language-python">key = [&#39;Alex&#39;, &#39;Jess&#39;, &#39;Dilan&#39;, &#39;Mei&#39;, &#39;Teddy&#39;]
value = [80, 90, 95, 67, 88]
key_val = [key, value]   ##2중 어레이[[&#39;Alex&#39;, &#39;Jess&#39;, &#39;Dilan&#39;, &#39;Mei&#39;, &#39;Teddy&#39;], [80, 90, 95, 67, 88]]
##zip(*변수)로 unzip을 하여 dict로 변환
landmark_dict = dict(zip(*key_val))</code></pre>
<h3 id="출력-결과">출력 결과</h3>
<blockquote>
</blockquote>
<pre><code class="language-python">{&#39;Alex&#39;: 80, &#39;Jess&#39;: 90, &#39;Dilan&#39;: 95, &#39;Mei&#39;: 67, &#39;Teddy&#39;: 88}</code></pre>
<h3 id="----dictionary를-list-2개-key-value로-생성----">--- dictionary를 list 2개 key, value로 생성 ---</h3>
<blockquote>
</blockquote>
<pre><code class="language-python">my_dict = {&#39;a&#39;: 1, &#39;b&#39;: 2, &#39;c&#39;: 3}
keys = list(my_dict.keys())
values = list(my_dict.values())</code></pre>
<h3 id="출력-결과-1">출력 결과</h3>
<blockquote>
</blockquote>
<pre><code class="language-python">print(keys)   # [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;]
print(values) # [1, 2, 3]</code></pre>
<h3 id="----dictionary를-zip을-활용하여-list-2개-key-value로-생성----">--- dictionary를 zip(*)을 활용하여 list 2개 key, value로 생성 ---</h3>
<blockquote>
<pre><code class="language-python">keys, values = zip(*my_dict.items()) # zip으로 생성된 결과는 튜플이므로 list로 변환
keys = list(keys)
values = list(values)
print(keys)   #[&#39;a&#39;, &#39;b&#39;, &#39;c&#39;]
print(values) #[1, 2, 3]</code></pre>
</blockquote>
<pre><code>
### --- 리스트 컴프리핸션을 이용하여 변환 ---
&gt;
```python
    my_dict = {&#39;a&#39;: 1, &#39;b&#39;: 2, &#39;c&#39;: 3}
    keys = [k for k in my_dict.keys()]
    values = [v for v in my_dict.values()]
    print(keys)   # [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;]
    print(values) # [1, 2, 3]</code></pre><h2 id="문자열-str">문자열 (str)</h2>
<h3 id="문자열-내장함수">문자열 내장함수</h3>
<p><strong>▶ lower ▶ upper</strong></p>
<blockquote>
<pre><code class="language-python">s = &quot;HapPineSS&quot;
#소문자 casting된 문자열 반환
s=s.lower()  # happiness
s=s.upper() # HAPPINESS</code></pre>
</blockquote>
<pre><code>
**▶ find  ▶ count**
&gt;```python
# &quot;s&quot;가 위치한 index 찾기
# 여러 개일 경우 가장 앞쪽 index
print(s.find(&quot;s&quot;)) # 7
# 문자열 내의 &quot;s&quot;의 개수
print(s.count(&quot;s&quot;)) # 2</code></pre><p><strong>▶ replace ▶ split</strong></p>
<blockquote>
<pre><code class="language-python">#문자 변환
s = &quot;Hello, World!&quot;
s_new = s.replace(&quot;World&quot;, &quot;Python&quot;)  # &quot;Hello, Python!&quot;
#주어진 문자열을 쉼표로 분할하여 리스트로 변환
s = &quot;apple,orange,banana&quot;
words = s.split(&#39;orange&#39;)  ##[&#39;apple&#39;, &#39;orange&#39;, &#39;banana&#39;]</code></pre>
</blockquote>
<pre><code>
### 문자열 2개 연결하기
&gt;```python
a = &quot;ABC&quot; + &quot;DEF&quot;
b = &quot;Hello&quot;
c = &quot;World&quot;
print(a) # ABCDEF
print(b + &quot; &quot; + c) # Hello World</code></pre><h2 id="리스트list">리스트(list)</h2>
<p><strong>▶ 빈 리스트 생성 ▶ 리스트 2개 연결</strong> </p>
<blockquote>
<pre><code class="language-python">arr1 = []
arr2 = list() #2가지 모두 빈 리스트가 생성된다.
arr1 = [1, 2]
arr2 = [3, 4, 5]
arr = arr1 + arr2 # [1, 2, 3, 4, 5]</code></pre>
</blockquote>
<p><strong>▶ 리스트 생성과 동시에 초기화 ▶ append</strong></p>
<blockquote>
<pre><code class="language-python">arr = []
for i in range(1,11):
    arr.append(i)
print(arr) # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 생성
arr2 = list(range(1, 11))
print(arr2) # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 생성</code></pre>
</blockquote>
<pre><code>
**▶ insert**
&gt;```python
# arr.insert(i, v) : i번 인덱스에 v 추가
arr.insert(3, 7) # [1, 2, 3, 7, 4, 5, 6, 7, 8, 9, 10]</code></pre><p><strong>▶ pop ▶ remove ▶ del</strong></p>
<blockquote>
<pre><code class="language-python"></code></pre>
</blockquote>
<h5 id="arr1pop--마지막-원소-꺼내기-꺼낸-값-반환">arr1.pop() : 마지막 원소 꺼내기 (꺼낸 값 반환)</h5>
<h5 id="arr1popi--i번-인덱스-값-꺼내기-꺼낸-값-반환">arr1.pop(i) : i번 인덱스 값 꺼내기 (꺼낸 값 반환)</h5>
<h5 id="arrpop--마지막-원소-꺼내기-꺼낸-값-반환">arr.pop() : 마지막 원소 꺼내기 (꺼낸 값 반환)</h5>
<h5 id="arrpopi--i번-인덱스-값-꺼내기-꺼낸-값-반환">arr.pop(i) : i번 인덱스 값 꺼내기 (꺼낸 값 반환)</h5>
<p>print(arr.pop()) # 10
print(arr) # [1, 2, 3, 7, 4, 5, 6, 7, 8, 9]
print(arr.pop(3)) # 7
print(arr) # [1, 2, 3, 4, 5, 6, 7, 8, 9]</p>
<h5 id="arr1removev--v-값을-찾아서-제거">arr1.remove(v) : v 값을 찾아서 제거</h5>
<h5 id="없는-값을-제거할-경우-valueerror-listremovex-x-not-in-list-에러-발생">없는 값을 제거할 경우 ValueError: list.remove(x): x not in list 에러 발생</h5>
<p>arr.remove(5) # [1, 2, 3, 4, 6, 7, 8, 9]
print(arr)
del arr[6]   # 6번 (7번째) index 원소 삭제
print(arr) # [1, 2, 3, 4, 6, 7, 9]</p>
<pre><code>
**▶ index ▶ min, max, sum**
&gt;```python
print(arr.index(7)) # 5  7이라는 값의 인덱스 순서
print(sum(arr)) # a의 모든 원소의 합 32
print(max(arr)) # a의 원소 중 최대 값 9
print(min(arr)) # a의 원소 중 최소 값 1</code></pre><p><strong>▶ sort ▶ reverse</strong></p>
<blockquote>
<pre><code class="language-python">a = [1, 3, 2, 7, 5] # 원본 리스트 정렬 (리스트 a 자체 정렬)
a.sort() # 오름차순 [1, 2, 3, 5, 7]
a.sort(reverse=True) # 내림차순 [7, 5, 3, 2, 1]</code></pre>
</blockquote>
<h5 id="정렬된-새로운-리스트-반환-리스트-a는-원본-유지-정렬된-리스트-b로-반환">정렬된 새로운 리스트 반환 (리스트 a는 원본 유지, 정렬된 리스트 b로 반환)</h5>
<p>a = [1, 3, 2, 7, 5]
b = sorted(a)
c = sorted(a, reverse=True) # 내림차순
print(a) # [1, 3, 2, 7, 5]
print(b) # [1, 2, 3, 5, 7]
print(c) # [7, 5, 3, 2, 1]
a = [1, 3, 2, 7, 5] #### 리스트 원소 순서 뒤집기
a.reverse() # [5, 7, 2, 3, 1]</p>
<pre><code>
**▶ clear**
&gt;```python
a.clear() # 빈 리스트로 만들기</code></pre><h3 id="리스트-활용하기">리스트 활용하기</h3>
<p><strong>▶ len</strong></p>
<blockquote>
<pre><code class="language-python">a = [1, 2, 3, 4]
len(a) # 리스트 원소의 개수 출력. 4
for i in range(len(a)):
  print(a[i])
for x in a:
  print(x)</code></pre>
</blockquote>
<pre><code>
**▶ enumerate**
&gt;```python
a = [12, 19, 34, 21, 50]
for x in enumerate(a):
  print(x) # (0, 12)\n (1, 19)\n ... (4, 50)\n 튜플로 출력
for x in enumerate(a):
  print(x[0], x[1]) # 0 12\n 1 19\n ... 4 50\n 
for i, val in enumerate(a):
  print(i, val) # 0 12\n 1 19\n ... 4 50\n</code></pre><p><strong>▶ all, any</strong></p>
<blockquote>
<pre><code class="language-python">a = [11, 12, 42, 38, 7] ## all(iterable 리스트, 튜플, 딕셔너리 등)</code></pre>
</blockquote>
<h2 id="모든-요소가-참이면-true-하나라도-거짓이면-false">모든 요소가 참이면 True, 하나라도 거짓이면 False</h2>
<p>a = [11, 12, 42, 38, 7]
if all(60 &gt; x for x in a):
  print(&quot;모든 원소가 60 미만입니다.&quot;) ## all(iterable 리스트, 튜플, 딕셔너리 등)</p>
<h2 id="요소가-하나라도-참이면-true-전부-거짓이면-false">요소가 하나라도 참이면 True, 전부 거짓이면 False</h2>
<p>if any(10 &gt; x for x in a):
  print(&quot;20미만인 원소가 존재합니다.&quot;)</p>
<pre><code>

## 2차원 리스트
**▶2차원 리스트 초기화**
&gt;```python
# N*M 2차원 리스트 초기화 할 때 (세로 N &amp; 가로 M)
n = 5  # 세로 크기
m = 4  # 가로 크기
default_value = 0
# N*M 2차원 리스트 초기화 (세로 N &amp; 가로 M)
array = [[default_value] * m for _ in range(n)]
# 결과 확인을 위한 출력
for row in array:
    print(row)
## [0, 0, 0, 0]
## [0, 0, 0, 0]
## [0, 0, 0, 0]
## [0, 0, 0, 0]
## [0, 0, 0, 0]</code></pre><p><strong>▶특정 영역의 값 계산 ▶각 행의 합</strong></p>
<blockquote>
<pre><code class="language-python">n = 4
m = 4
array = [ [1, 2, 3, 4],
    [5, 6, 7, 8],
    [9, 10, 11, 12],
    [13, 14, 15, 16] ] ### (1, 1)에서 (2, 2)까지의 합 (인덱스는 0부터 시작)
sum_value = 0
for i in range(1, 3):
    for j in range(1, 3):
        sum_value += array[i][j]
print(&quot;Sum of submatrix:&quot;, sum_value) ##34  [6,7] [10,11]의 합
####</code></pre>
</blockquote>
<h4 id="각-행의-합">각 행의 합</h4>
<p>for row in array:
    row_sum = sum(row)
    print(&quot;Row sum:&quot;, row_sum)
##Row sum: 10
##Row sum: 26
##Row sum: 42
##Row sum: 58</p>
<pre><code>
## 튜플 (tuple)
**▶튜플 생성 및 초기화 ▶튜플에 원소 추가**
&gt;```python
t = (1, 2, 3, 4, 5) ##생성
t1 = (1, 2, 3)
t2 = (4, 5)
t3 = t1 + t2</code></pre><p><strong>▶튜플 인덱스로 값 조회 ▶튜플 값 존재 여부 확인</strong></p>
<blockquote>
<pre><code class="language-python">print(t[3]) # 4
print(t[1:4]) # (2, 3, 4)
t[3] = 2 # Error. 변경 불가, 불가능한 문법
print(1 in t) # True
print(9 in t) # False</code></pre>
</blockquote>
<pre><code>
**▶ count ▶ index**
&gt;```python
t = (1, 2, 2, 3, 3, 4)
print(t.count(2)) ## &quot;2&quot;의 갯수 반환
print(t.index(4)) ## 특정 값의 인덱스 반환. 
# 여러 개면 가장 앞쪽 인덱스, 없으면 에러
print(t.index(2)) # 1</code></pre><h3 id="딕셔너리-dict">딕셔너리 (dict)</h3>
<p><strong>▶‘key’로 문자열, 정수형, 실수형, 불린형 모두 사용 가능
▶‘value’로는 추가로 리스트, 딕셔너리까지도 사용 가능
▶딕셔너리 초기화</strong></p>
<blockquote>
<pre><code class="language-python"></code></pre>
</blockquote>
<h3 id="dict-초기화">dict 초기화</h3>
<p>data = dict()
data[&#39;사과&#39;] = &#39;Apple&#39;
data[&#39;바나나&#39;] = &#39;Banana&#39;
data[&#39;코코넛&#39;] = &#39;Coconut&#39;</p>
<h4 id="딕셔너리-key만-value만-리스트로">딕셔너리 key만, value만 리스트로</h4>
<p>data.keys() # key만   ##출력 : dict_keys([&#39;사과&#39;, &#39;바나나&#39;, &#39;코코넛&#39;])
data.values() # value만    ##출력: dict_values([&#39;Apple&#39;, &#39;Banana&#39;, &#39;Coconut&#39;])</p>
<pre><code>
### 집합(set)
**▶ add ▶ update ▶ remove**
&gt;```python
s = {1, 2, 3}
# set.add(v) 새로운 원소 추가
s.add(4) # {1, 2, 3, 4}
#####
# set.update(v) 새로운 원소 여러 개 추가 (수정 보다는 여러 개 추가 개념)
s.update([5,7]) # {1, 2, 3, 4, 5, 7}
#####
a = {1, 2, 3}
# set.remove(v) 원소 삭제
s.remove(4) # {1, 2, 3, 5, 7}</code></pre><h2 id="python-코테-cheat-sheet">Python 코테 cheat sheet</h2>
<p><strong>한 줄에 여러 개의 숫자 입력 받기</strong></p>
<blockquote>
<pre><code class="language-python"></code></pre>
</blockquote>
<p><del>[입력 예시]</del>
1 2 3 4
a, b, c, d = map(int, input().split())
print(a, b, c, d) # 1, 2, 3, 4</p>
<p><del>[입력 예시]</del>
1 3 5 7 9 10
a = list(map(int, input().split()))
print(a) # [1, 3, 5, 7, 9, 10]</p>
<p><del>[입력 예시]</del>
A B C D E
a = list(input().split())
print(a) # [&#39;A&#39;, &#39;B&#39;, &#39;C&#39;, &#39;D&#39;, &#39;E&#39;]</p>
<pre><code>
**리스트 중복 제거 &amp; 정렬**
&gt;```python
a = [3, 1, 5, 8, 5, 10, 7, 1]
print(a)
b = list(set(a)) # [1, 3, 5, 7, 8, 10]
c = sorted(list(set(a))) # [1, 3, 5, 7, 8, 10]
d = sorted(list(set(a)), reverse=True) # [10, 8, 7, 5, 3, 1]</code></pre><p><strong>리스트 전체 합과 평균 구하기</strong></p>
<blockquote>
<pre><code class="language-python">N = int(input()) # 개수
a = list(map(int, input().split())) # 점수 리스트 입력
total = sum(a)
avg = int(round(total/N, 0)) # 평균을 첫째 자리에서 반올림</code></pre>
</blockquote>
<pre><code>
**리스트 반복문에서 리스트의 원소 여러 번 사용할 때**
&gt;```python
score = [80, 83, 95, 87, 93, 71]
for i, val in range(N):
  if val &lt; 90: # &quot;score[i] &lt; 90&quot; 대신 짧고 편하게 사용 가능
    print(&quot;B&quot;)
  elif val &lt; 80:
    print(&quot;C&quot;)
  elif val &lt; 70:
    print(&quot;D&quot;)</code></pre><p> <strong>리스트 원소 개수 세기 (딕셔너리 활용)</strong></p>
<blockquote>
<pre><code class="language-python"> s = [1, 3, 4, 2, 3, 4, 1, 3]
cnt = {} # dictionary 생성
for x in s:
  if x in cnt:
    cnt[x] += 1
  else:
    cnt[x] = 1</code></pre>
</blockquote>
<pre><code>
**딕셔너리 value로 key값 찾기**
&gt;```python
for key, value in cnt.items():
  if value == &quot;찾을 value 값&quot;:
    print(key, end=&#39; &#39;) # value에 해당하는 key 값 출력</code></pre><p><strong>두 변수 값 바꾸기 (swap) + 딕셔너리 ‘key’ 또는 ‘value’로 존재 여부 확인</strong></p>
<blockquote>
<pre><code class="language-python">##swap
a,b = b,a</code></pre>
</blockquote>
<h1 id="키-존재-여부-확인">키 존재 여부 확인</h1>
<p>if &#39;사과&#39; in data:
    print(&quot;키 &#39;사과&#39;가 존재합니다.&quot;)</p>
<h1 id="값-존재-여부-확인">값 존재 여부 확인</h1>
<p>if 3 in data.values():
    print(&quot;값 3이 존재합니다.&quot;)</p>
<pre><code>
**딕셔너리를 key 또는 value 기준으로 정렬**
&gt;```python
a = {&#39;Tom&#39;: 90, &#39;Liz&#39;: 75, &#39;John&#39;: 67, &#39;Mia&#39;: 92}
print(a.keys()) # dict_keys([&#39;Tom&#39;, &#39;Liz&#39;, &#39;John&#39;, &#39;Mia&#39;])
print(list(a.keys())) # [&#39;Tom&#39;, &#39;Liz&#39;, &#39;John&#39;, &#39;Mia&#39;]
print(sorted(a.keys())) # [&#39;John&#39;, &#39;Liz&#39;, &#39;Mia&#39;, &#39;Tom&#39;]
print(sorted(a.keys(), reverse=True)) # [&#39;Tom&#39;, &#39;Mia&#39;, &#39;Liz&#39;, &#39;John&#39;]
# 튜플 자료형으로 리턴
print(sorted(a.items())) 
# 결과 : [(&#39;John&#39;, 67), (&#39;Liz&#39;, 75), (&#39;Mia&#39;, 92), (&#39;Tom&#39;, 90)]
print(sorted(a.items(), reverse=True)) 
# 결과 : [(&#39;Tom&#39;, 90), (&#39;Mia&#39;, 92), (&#39;Liz&#39;, 75), (&#39;John&#39;, 67)]
# 람다식 key값 기준 정렬
print(sorted(a.items(), key=lambda x: x[0])) 
# 결과 : [(&#39;John&#39;, 67), (&#39;Liz&#39;, 75), (&#39;Mia&#39;, 92), (&#39;Tom&#39;, 90)]
print(sorted(a.items(), key=lambda x: x[0], reverse=True)) 
# 결과 : [(&#39;Tom&#39;, 90), (&#39;Mia&#39;, 92), (&#39;Liz&#39;, 75), (&#39;John&#39;, 67)]
# 람다식 value값 기준 정렬
print(sorted(a.items(), key=lambda x: x[1]))
# 결과 : [(&#39;John&#39;, 67), (&#39;Liz&#39;, 75), (&#39;Tom&#39;, 90), (&#39;Mia&#39;, 92)]
print(sorted(a.items(), key=lambda x: x[1], reverse=True))
# 결과 : [(&#39;Mia&#39;, 92), (&#39;Tom&#39;, 90), (&#39;Liz&#39;, 75), (&#39;John&#39;, 67)]</code></pre><p><strong>2차원 리스트 입력 받기</strong></p>
<blockquote>
<pre><code class="language-python">&#39;&#39;&#39;
[입력 예시]
5
0 2 1 1 0
1 1 1 1 2
0 2 1 2 1
0 2 1 1 0
0 1 1 1 2
&#39;&#39;&#39;
n = int(input())
arr = list(list(map(int, input().split())) for _ in range(n))</code></pre>
</blockquote>
<pre><code>
**이분 탐색**
중앙값을 찾아서 비교해서 중앙값 전/후로 나누어서 다시 탐색을 반복
&gt;```python
a.sort() # 먼저 정렬
lt = 0 # 시작 값
rt = n-1 # 끝 값
##와일문 제한
while lt &lt;= rt:
  mid = (lt + rt) // 2
  if a[mid] &lt; m:
    lt = mid + 1
  elif m &lt; a[mid]:
    rt = mid - 1
  elif m == a[mid]:
    print(mid+1)
    break</code></pre><p><strong>python list 2개로 dictionary를 생성하기</strong></p>
<blockquote>
<pre><code class="language-python">key = [&#39;Alex&#39;, &#39;Jess&#39;, &#39;Dilan&#39;, &#39;Mei&#39;, &#39;Teddy&#39;]
value = [80, 90, 95, 67, 88]
key_val = [key, value]
landmark_dict = dict(zip(*key_val))</code></pre>
</blockquote>
<h3 id="결과--alex-80-jess-90-dilan-95-mei-67-teddy-88">결과 : {&#39;Alex&#39;: 80, &#39;Jess&#39;: 90, &#39;Dilan&#39;: 95, &#39;Mei&#39;: 67, &#39;Teddy&#39;: 88}</h3>
<pre><code>
**python list가 비어있는지 확인**
&gt;```python
arr = []
if not arr:
  print(&quot;empty&quot;)
 ##
if arr:
  print(&quot;not empty&quot;)</code></pre><p><strong>python list의 마지막 요소 가져오기/제거하기</strong></p>
<blockquote>
<pre><code class="language-python">arr = [1, 3, 5, 2, 4]
print(arr[-1]) # 4
arr.pop()
print(arr)  ##[1, 3, 5, 2]</code></pre>
</blockquote>
<pre><code>
**str -&gt; 중복제거 set -&gt; String으로 변환 **
&gt;```python
my_string = &quot;aavvccccddddeee&quot;
# converting the string to a set
temp_set = set(my_string)
# stitching set into a string using join
new_string = &#39;&#39;.join(temp_set)
print(new_string)
## 결과 :vcdae</code></pre><p><strong>열을 짧게 구성 + 빠르게 행렬 구성</strong> </p>
<blockquote>
<pre><code class="language-python">n = 3
my_list = [0]*n # n denotes the length of the required list</code></pre>
</blockquote>
<h1 id="0-0-0-0">[0, 0, 0, 0]</h1>
<p>print(my_list)
print([my_list]*3)</p>
<p>```</p>
<h3 id="도움될만한-링크--httpsvelogiojeong_yooonycode-up-ed8c8cec9db4ec8dac-100eca09c">도움될만한 링크 : <a href="https://velog.io/@jeong_yooony/Code-Up-%ED%8C%8C%EC%9D%B4%EC%8D%AC-100%EC%A0%9C">https://velog.io/@jeong_yooony/Code-Up-%ED%8C%8C%EC%9D%B4%EC%8D%AC-100%EC%A0%9C</a></h3>
<p>참조 링크:
<a href="https://choiiis.github.io/python/for-coding-test/">https://choiiis.github.io/python/for-coding-test/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Mastering Spring Web 101 Workshop 10기 후기 ]]></title>
            <link>https://velog.io/@path_creator/Mastering-Spring-Web-101-Workshop-%ED%9B%84%EA%B8%B0</link>
            <guid>https://velog.io/@path_creator/Mastering-Spring-Web-101-Workshop-%ED%9B%84%EA%B8%B0</guid>
            <pubDate>Tue, 13 Aug 2024 04:48:18 GMT</pubDate>
            <description><![CDATA[<h1 id="mastering-spring-web-101-구성">Mastering Spring Web 101 구성</h1>
<h3 id="일정--2024892024811">일정 : 2024.8.9~2024.8.11</h3>
<p><strong>금요일 저녁에 시작하여 3시간</strong> 정도의 온라인 강의, <strong>토요일, 일요일 (아침 9시부터 6시까지)</strong></p>
<h3 id="교육-구성-">교육 구성 :</h3>
<p><strong>Todo 스케줄 앱</strong>을 개발하는 과정을 진행한다. 이미 짜여진 코드(프론트엔드 코드, 엔티티, 기본적이고 단순한 서비스 코드들)를 기반으로 설계와 코드 작성을 활용하여 스프링 부트의 많은 것을 배운다. 명심해야 할건 단순하고 쉽게하는 api개발은 아니다.</p>
<h3 id="진행-강사멘토님-">진행 강사(멘토)님 :</h3>
<p>모 유명 기업의 테크 리드로 계신 <strong>박용권</strong>님이며, 총 경력은 거의 20년이 좀 안 되는 경력을 가지고 계신다. 개발자들은 경력이 오래될수록 개발을 안 해서 코딩 퍼포먼스가 떨어진다고 하는데, 그런 느낌의 오래된 개발자는 아니셨고, 여전히 기술을 많이 활용하시고 응용할 줄 아는 개발자였다.</p>
<p>수강생은 10명정도로 소수로 이루어지는 교육이므로 쉽게 질의 응답이 가능한 교육이었다. 난 많이 버벅였지만 그래도 확실한 멘토링을 해주셨다. 참고로 이전부터 교육하신 경험이 많아서인지, 굉장히 빠른 템포로 코드 제작 및 개념을 보여주시는데, 따라가기엔 벅차지 않을 정도로 따라가기가 편하였었다.</p>
<p>아래 교육 구성 참고 자료
<a href="https://springrunner.dev/training/mastering-spring-web-101-workshop/">https://springrunner.dev/training/mastering-spring-web-101-workshop/</a></p>
<p><strong>Web과 Servlet에 관련된 개발을 중점으로 배운다.</strong></p>
<p><img src="https://velog.velcdn.com/images/path_creator/post/0c06e587-29bf-4f54-a899-c157ecaa391d/image.png" alt=""></p>
<h1 id="스프링-러너를-마치며">스프링 러너를 마치며…</h1>
<p>생각한 것보다 훨씬 어려운 수업이었다. 처음엔 기본 관련 수업이라길래, Todo 스케줄 서비스를 REST API로 짜는 그냥 그런 강의일 줄 알았다. 솔직히, 큰 기대 없이 REST API를 짜는 것에 대한 리뷰나 한 번 더 하자는 생각으로 지원했다. 하지만 생각보다 수많은 설정(Config), 예외 처리(Exception Handler), 및 데이터 소스를 자유자재로 활용하여 코드를 간결화하고, 구현적인 부분에서도 흠잡을 데 없는 스프링의 REST API 설계 수업이었다. 코드와 함께하는 짧은 강의 시간 안에 상당히 많은 모듈과 구성된 클래스를 많이 사용하기에, 스프링 부트 경력이 1년이 채 안 된 사람들에게는 상당히 어려울 수 있는 수업이다. 하지만 경력이 짧더라도 꽤 많은 지혜를 습득할 수 있는 시간이라 아깝지는 않은 시간이었다. 오류 유발 가능성을 낳는 구조나 개발에 관련해서도 많은것 들을 들을수 있다. 지식을 배우기도 하지만, 지혜나 구조적인 사고 또한 배울 수 있기 때문에 꽤 가치 있는 시간이었다.</p>
<h2 id="감명깊게-배우고-깨달은-것">감명깊게 배우고 깨달은 것?</h2>
<h3 id="--스프링-부트의-공부법">- 스프링 부트의 공부법</h3>
<p>스프링의 활용과 공부법에 대해 배웠다. 단순한 공부가 아니라 꽤 높은 위치에 올랐을 때도 활용할 수 있는 방법에 대해 배운다. 기본+기본이 합해져 응용이 되고, 그 응용을 어떻게 활용하는지를 많이 배운다. 그리고 스프링 부트의 애노테이션을 해부하는 과정을 통해 어떤 구조로 이루어지고 어떻게 활용되는지를 배웠던 것이 좋았다.</p>
<h3 id="--코드를-간결하게하는-스프링-부트-활용-지식">- 코드를 간결하게하는 스프링 부트 활용 지식</h3>
<p>복잡한 스프링 부트의 구성으로 이루어진 코드를 팍팍 줄여버리는 응용 애노테이션 활용과 다양한 스프링의 모듈 활용이 굉장히 놀라운 장면이 많았다. 기본적인 라이브러리 활용과 기본 개념을 채우기 바쁜 나에게는 다소 과분한 지식과 기술들이었을 수도 있었겠지만, 이것을 배운 덕분에 앞으로 좀 더 세련된 코드를 만들 능력을 키울 수 있을 것 같았다.</p>
<h3 id="--ide-버젼-오류나-여러-포맷등으로-이루어지는-오류-해결법">- IDE 버젼 오류나 여러 포맷등으로 이루어지는 오류 해결법</h3>
<p>개발자들이 자주 겪는 애매한 한/영 깨짐 현상 오류나 기타 버전등의 오률류를 해결 할 수 있는 부분을 많이 배웠다. 떠하여 IDE 때문에 생길 수 있는 버젼오류 등 그런것들을 단번에 해결할 수 있느 부분에 대해서도 질의 응답을 잘 해 주셨다. 이것도 지식이라면 지식이겠지만 내가볼땐 지식이라기 보다 경험+해결 능력에 가까운 느낌이었다. 이런 것들이 모여 나중에 경험 들이 될거라고 본다. </p>
<h3 id="예">예)</h3>
<p><img src="https://velog.velcdn.com/images/path_creator/post/1afc25f3-755c-409b-b4cd-baf7ac7a3942/image.png" alt="">
<img src="https://velog.velcdn.com/images/path_creator/post/ca55b751-6ab0-4ab5-8d7b-ae2f261c2729/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/path_creator/post/e18ce6bd-0b1e-4e9e-8384-c7987a880d5e/image.png" alt=""></p>
<h3 id="스프링-러너-교육을-고민하는-분께-전하고-싶은-말">스프링 러너 교육을 고민하는 분께 전하고 싶은 말</h3>
<p>그냥 단순히 쉽게 듣고 만들고 뻔한 API 개발론을 적용하는 교육이 아니었다. 그런 기대를 하고 교육을 하기보다는 어떻게 구성해야 오류 예방을 최대한하고 어떤 구성이 리팩토링에 유리한 개발인지, 그리고 수십 줄의 코드를 한 번에 4~5줄 코드로 만드는 스프링 부트 모듈을 최대한 활용하는 방법을 가르친다. 더불어 질의 응답을 통해 수많은 지혜와 스프링 부트 공부의 방법을 배울 수 있는 수업이다. 구지 참고 사항이 있다면 단축키는 잘 활용하는 상태에서 가고, 컨트롤러나 exception, servelet등 관련 어노테이션의 구조 정의 사용 이유에 대해서는 많이 알아두고 가는걸 추천한다. 어떻게 사용하는지 말고, 개념과 구조를 잘 파악하고 가라는 거다. 그런다면 더 많은 것들을 배울 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백엔드에서 자주 사용되는 디자인패턴 -행위 패턴형-]]></title>
            <link>https://velog.io/@path_creator/%EB%B0%B1%EC%97%94%EB%93%9C%EC%97%90%EC%84%9C-%EC%9E%90%EC%A3%BC-%EC%82%AC%EC%9A%A9%EB%90%98%EB%8A%94-%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4-%EA%B5%AC%EC%A1%B0-%ED%8C%A8%ED%84%B4%ED%98%95-nayvgs1w</link>
            <guid>https://velog.io/@path_creator/%EB%B0%B1%EC%97%94%EB%93%9C%EC%97%90%EC%84%9C-%EC%9E%90%EC%A3%BC-%EC%82%AC%EC%9A%A9%EB%90%98%EB%8A%94-%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4-%EA%B5%AC%EC%A1%B0-%ED%8C%A8%ED%84%B4%ED%98%95-nayvgs1w</guid>
            <pubDate>Fri, 09 Aug 2024 04:16:07 GMT</pubDate>
            <description><![CDATA[<h3 id="span-stylebackground-color-rgba1020050005----행위-패턴-----span"><span style="background-color: rgba(10,200,500,0.5)">--- <em>행위 패턴</em> ----</span></h3>
<h4 id="깃허브-링크--httpsgithubcomjinhan-han-jeremyrealdesignpattern">깃허브 링크 : <a href="https://github.com/Jinhan-Han-Jeremy/RealDesignPattern">https://github.com/Jinhan-Han-Jeremy/RealDesignPattern</a></h4>
<h3 id="span-stylebackground-color-rgba1020010005---------전략-스트레티지strategy---------span"><span style="background-color: rgba(10,200,100,0.5)">-------- 전략, 스트레티지(Strategy) --------</span></h3>
<p>행동을 클래스로 캡슐화해서 동적으로 행동을 바꿀 수 있게 하는 패턴</p>
<ul>
<li><strong>전략 패턴을 사용하는 이유</strong></li>
<li><ul>
<li>객체 책임과 행동이 상황에 따라 다양한 기능이 빈번하게 추가/삭제되는 경우.</li>
</ul>
</li>
<li><ul>
<li>객체의 결합을 통해 기능이 생성될 수 있는 경우.</li>
</ul>
</li>
<li><ul>
<li>객체를 사용하는 코드를 손상시키지 않고 런타임에 객체에 추가 동작을 할당할 수 있어야 하는 경우</li>
</ul>
</li>
<li><ul>
<li>상속을 통해 서브클래싱으로 객체의 동작을 확장하는 것이 어색하거나 불가능 할 때</li>
</ul>
</li>
</ul>
<p>*<em>자주 사용되는 경우 : *</em> 로깅(Logging), 트랜잭션 관리(Transaction Management), 정보 캐싱(Information Caching), 메소드 접근 제한(Method Access Control), 서비스 기능 확장(Extending Service Methods), 사용자 정의 데코레이터를 사용한 알림 서비스(Custom Decorator for Notification Service
Scenario)</p>
<p><strong>스트레티지(Strategy) 장점</strong></p>
<ul>
<li>공통 로직이 부모 클래스에 있지 않고 Context 라는 별도의 클래스에 존재하기 때문에 구현체들에 대한 영향도가 적음</li>
<li>Context 가 Strategy 라는 인터페이스를 의존하고 있기 때문에 구현체를 갈아끼우기 쉬움</li>
</ul>
<p><strong>스트레티지(Strategy) 단점</strong></p>
<ul>
<li>로직이 늘어날 때마다 구현체 클래스가 늘어남</li>
<li>Context 와 Strategy 를 한번 조립하면 전략을 변경하기 힘듬</li>
</ul>
<p><strong>템플릿 메서드(Templage Method)</strong>
일정 작업을 처리하는 부분을 서브클래스로 캡슐화해서 전체 수행 구조는 바꾸지 않으면서 특정 단계만 변경해서 수행하는 패턴</p>
<p><strong>전략 패턴이</strong> 템플릿 메소드보다 유연하고 좋음
<strong>전략 패턴 :</strong> 합성(composition)을 통해 해결책을 강구하며, 대부분 인터페이스를 사용 </p>
<ul>
<li>클라이언트와 객체 간의 결합이 느슨함</li>
</ul>
<p><strong>템플릿 메서드 패턴 :</strong> 상속(inheritance)을 통해 해결책을 제시하며, 주로 추상 클래스나 구체적인 클래스를 사용</p>
<ul>
<li>두 모듈이 더 밀접하게 결합 (결합도가 높으면 안좋음)</li>
</ul>
<blockquote>
<p>전략 패턴 예제) <strong>텍스트를 입력 받고 서치하는 인터페이스와 오브젝트 구성</strong></p>
</blockquote>
<ol>
<li>*<em>public interface SearchStrategy { : *</em>검색 기능을 하는 인터페이스 구성<pre><code class="language-java">// SearchStrategy 인터페이스: 다양한 검색 전략을 위한 공통 인터페이스 정의
public interface SearchStrategy {
 // 텍스트에서 패턴을 검색하는 메서드
 // 검색된 패턴의 시작 인덱스를 반환, 패턴이 없으면 -1 반환
 int search(String text, String pattern);
}</code></pre>
</li>
</ol>
<blockquote>
<p>전략 패턴 예제) <strong>알고리즘 서비스를 구성하는 클래스</strong>
2. <strong>public class RegexSearch implements SearchStrategy { :</strong> RegexSearch기능을 하는 함수 생성 및 @Override</p>
</blockquote>
<pre><code class="language-java">import java.util.regex.Matcher;
import java.util.regex.Pattern;
// RegexSearch 클래스: 정규 표현식을 이용한 검색 전략 구현
public class RegexSearch implements SearchStrategy {
    @Override
    public int search(String text, String pattern) {
        // Pattern과 Matcher 클래스를 사용하여 정규 표현식 검색 수행
        Pattern p = Pattern.compile(pattern);
        Matcher m = p.matcher(text);
        if (m.find()) {
            // 패턴이 발견되면 시작 인덱스 반환
            return m.start();
        }
        // 패턴이 발견되지 않으면 -1 반환
        return -1;
    }
}</code></pre>
<ol start="3">
<li>*<em>public class SimpleSearch implements SearchStrategy { : *</em> SimpleSearch기능을 하는 인터페이스 구성<pre><code class="language-java">// SimpleSearch 클래스: 단순 문자열 검색 전략 구현
public class SimpleSearch implements SearchStrategy {
 @Override
 public int search(String text, String pattern) {
     // Java의 indexOf 메서드를 사용하여 단순 검색 수행
     return text.indexOf(pattern);
 }
}</code></pre>
</li>
</ol>
<blockquote>
<p>전략 패턴 예제) <strong>인터페이스 타입을 선택하는 클래스 구성</strong>
4. *<em>public class TextEditor { : *</em> 어떤 Search기능을 할당할지 지정해주는 클래스 구성
5. *<em>public void setSearchStrategy(SearchStrategy searchStrategy) { : *</em> setter 역할로 searchStrategy 알고리즘 선택
6. *<em>public int performSearch(String text, String pattern) { : *</em> 서치를 실행</p>
</blockquote>
<pre><code class="language-java">// TextEditor 클래스: 검색 전략을 사용하는 텍스트 편집기
public class TextEditor {
    // 현재 검색 전략을 저장하는 필드
    private SearchStrategy searchStrategy;
    // 검색 전략을 설정하는 메서드
    public void setSearchStrategy(SearchStrategy searchStrategy) {
        this.searchStrategy = searchStrategy;
    }
    // 설정된 검색 전략을 사용하여 텍스트에서 패턴을 검색하는 메서드
    public int performSearch(String text, String pattern) {
        if (searchStrategy == null) {
            // 검색 전략이 설정되지 않은 경우 예외 발생
            throw new IllegalStateException(&quot;Search strategy not set&quot;);
        }
        // 설정된 검색 전략을 사용하여 검색 수행
        return searchStrategy.search(text, pattern);
    }
}</code></pre>
<blockquote>
<p>전략 패턴 예제) <strong>메인 클래스 구성</strong></p>
</blockquote>
<pre><code class="language-java">import StrategyPattern.RegexSearch;
import StrategyPattern.SimpleSearch;
import StrategyPattern.TextEditor;
public class MainByStrategy {
    // 메인 메서드: 예제 실행
    public static void main(String[] args) {
        TextEditor editor = new TextEditor();
        // 예제 텍스트
        String text = &quot;Hello, this is a simple text editor.&quot;;
        // 단순 검색 전략 사용
        editor.setSearchStrategy(new SimpleSearch());
        int index = editor.performSearch(text, &quot;simple&quot;);
        System.out.println(&quot;Simple Search: &#39;simple&#39; found at index &quot; + index);
        // 정규 표현식 검색 전략 사용
        editor.setSearchStrategy(new RegexSearch());
        index = editor.performSearch(text, &quot;\\bsimple\\b&quot;);
        System.out.println(&quot;Regex Search: &#39;\\bsimple\\b&#39; found at index &quot; + index);
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/path_creator/post/0199d8e3-1256-4dc0-bc1f-f7d5ad865a06/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/path_creator/post/d7b61e2b-e67b-4cfb-8485-53dffcafc76a/image.png" alt=""></p>
<p>*<em>전체적 구조 : *</em></p>
<ul>
<li>전략 인터페이스{서치 기능(서비스 기능 담당)}</li>
<li>서치 알고리즘 클래스들 implements 전략 인터페이스{ @Override 서치 기능(서비스 기능 담당)}</li>
<li>서비스 조율 클래스{알고리즘 기능 선택 함수(전략 인터페이스), 데이터를 받고 서비스를 시행하는 함수(입력 텍스트, 패턴화된 찾으려는 텍스트) } 
<br/><br/></li>
</ul>
<h3 id="span-stylebackground-color-rgba1020010005---------옵저버observer---------span"><span style="background-color: rgba(10,200,100,0.5)">-------- 옵저버(Observer) --------</span></h3>
<p>객체의 상태 변화를 관찰하는 관찰자(옵저버) 목록을 객체에 등록해 상태가 변할 때마다 메서드 등을 통해 객체가 직접 옵서버에게 통지하게 하는 디자인 패턴</p>
<ul>
<li><strong>옵저버 패턴을 사용하는 이유</strong></li>
<li><ul>
<li>앱이 한정된 시간, 특정한 케이스에만 다른 객체를 관찰해야 하는 경우</li>
</ul>
</li>
<li><ul>
<li>대상 객체의 상태가 변경될 때마다 다른 객체의 동작을 트리거해야 할때</li>
</ul>
</li>
<li><ul>
<li>한 객체의 상태가 변경되면 다른 객체도 변경해야 할때. 그런데 어떤 객체들이 변경되어야 하는지 몰라도 될 때</li>
</ul>
</li>
</ul>
<p><strong>자주 사용되는 경우</strong> : 이벤트 기반 아키텍처 (Event-Driven Architecture), 실시간 데이터 업데이트 (Real-Time Data Update), 모니터링 시스템 (Monitoring System), 데이터베이스 변경 감지 (Database Change Notification), 사용자 인터페이스 (User Interface), 알림 시스템 (Notification System)</p>
<p><strong>옵저버(Observer) 장점</strong></p>
<ul>
<li>Subject의 상태 변경을 주기적으로 조회하지 않고 자동으로 감지 가능.</li>
<li>발행자의 코드를 변경하지 않고도 새 구독자 클래스를 도입할 수 있어 개방 폐쇄 원칙(OCP)Visit Website 준수.</li>
<li>런타임 시점에서에 발행자와 구독 알림 관계를 맺는게 가능.</li>
<li>상태를 변경하는 객체(Subject)와 변경을 감지하는 객체(Observer)의 관계를 느슨하게 유지 가능. (느슨한 결합)</li>
</ul>
<p><strong>옵저버(Observer) 단점</strong></p>
<ul>
<li>구독자는 알림 순서를 제어할수 없고, 무작위 순서로 알림을 받음</li>
<li><ul>
<li>하드 코딩으로 구현할수는 있겠지만, 복잡성과 결합성만 높아지기 때문에 추천되지는 않는 방법.</li>
</ul>
</li>
<li>옵저버 패턴을 자주 구성하면 구조와 동작을 알아보기 힘들어져 코드 복잡도가 증가.</li>
<li>다수의 옵저버 객체를 등록 이후 해지하지 않는다면 메모리 누수가 발생 가능.</li>
</ul>
<blockquote>
<p>옵저버 예제) <strong>유저의 날씨 조회 기록 관리</strong>
1.** interface Observer { :** 다른 클래스의 기록을 조회 가능하도록 하는 인터페이스.</p>
</blockquote>
<pre><code class="language-java">package Observer;
interface Observer {
    void display(WeatherAPI api);
}</code></pre>
<p>2.<strong>public class KoreanUser implements Observer { :</strong> 유저들의 날씨 조희 기록 클래스.
3.<strong>public void display(WeatherAPI api) { :</strong> 유저들의 날씨 조희 기록 형식 출력.</p>
<pre><code class="language-java">package Observer;
public class KoreanUser implements Observer {
    String name;
    public KoreanUser(String name) {
        this.name = name;
    }
    public void display(WeatherAPI api) {
        System.out.printf(&quot;%s님이 현재 날씨 상태를 조회함 : %.2f°C %.2fg/m3 %.2fhPa\n&quot;, name, api.temp, api.humidity, api.pressure);
    }
}</code></pre>
<blockquote>
<p>옵저버 예제) <strong>유저의 날씨 조회 기록 관리</strong>
4.** interface Subject { :** 객체들 관리 인터페이스</p>
</blockquote>
<pre><code class="language-java">package Observer;
interface Subject {
    void registerObserver(Observer o); // 구독 추가
    void removeObserver(Observer o); // 구독 삭제
    void notifyObservers(); // Subject 객체의 상태 변경시 이를 모든 옵저버에게 알림
}</code></pre>
<p>5.** public class WeatherAPI implements Subject{ :** 서브젝트 인터페이스 상속, 그리고 날씨 생성 및 유저 관리 서비스를 포함하는 클래스
6.** public void measurementsChanged() { :** 날씨 생성 기능
7.** @Override public void registerObserver(Observer o) { :** 옵저버로 구독자 등록
8.** public void notifyObservers() { :** 구독자 액티비티 기록 알림</p>
<pre><code class="language-java">package Observer;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class WeatherAPI implements Subject{
    float temp; // 온도
    float humidity; // 습도
    float pressure; // 기압
    // 구독자들을 담아 관리하는 리스트
    List&lt;Observer&gt; subscribers = new ArrayList&lt;&gt;();
    public void measurementsChanged() {
        // 현재의 온습도 데이터를 랜덤값으로 얻는 것으로 비유하였다.
        temp = new Random().nextFloat() * 100;
        humidity = new Random().nextFloat() * 100;
        pressure = new Random().nextFloat() * 100;
        notifyObservers(); // 온습도 값이 변화하면 바로 옵저버들에게 발행
    }
    @Override
    public void registerObserver(Observer o) {
        subscribers.add(o);
    }
    @Override
    public void removeObserver(Observer o) {
        subscribers.remove(o);
    }
    // 이벤트 전파
    @Override
    public void notifyObservers() {
        for(Observer o: subscribers) {
            o.display(this); // 자신의 객체를 매개변수로 줘서 현재 자신의 상태를 구독자에게 알림
        }
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/path_creator/post/102a6038-ee4a-4899-9ff6-748406be3be4/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/path_creator/post/6770e970-ba71-4c25-a14b-9c307749dcc7/image.png" alt=""></p>
<p>*<em>전체적 구조  : *</em></p>
<ul>
<li>옵저버 인터페이스 {기능}</li>
<li>기본 클래스 implements 옵저버 {생성자(변수들), 기능}</li>
<li>2번째 기본 인터페이스{기능들}</li>
<li>2번째 기본 클래스 implements 인터페이스{기본 클래스들을 관리하고 다루는 함수들}
<br/><br/></li>
</ul>
<p>참조 :
<a href="https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EC%B6%94%EC%83%81-%ED%8C%A9%ED%86%A0%EB%A6%ACAbstract-Factory-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90#%EC%B6%94%EC%83%81_%ED%8C%A9%ED%86%A0%EB%A6%AC_%ED%8C%A8%ED%84%B4_%ED%8A%B9%EC%A7%95">https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EC%B6%94%EC%83%81-%ED%8C%A9%ED%86%A0%EB%A6%ACAbstract-Factory-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90#%EC%B6%94%EC%83%81_%ED%8C%A9%ED%86%A0%EB%A6%AC_%ED%8C%A8%ED%84%B4_%ED%8A%B9%EC%A7%95</a></p>
<p><a href="https://innovation123.tistory.com/9#%EC%8B%B1%EA%B8%80%ED%86%A4%EC%9D%98%20%EB%AC%B8%EC%A0%9C%20%ED%95%B4%EA%B2%B0%20-%20statelsess-1">https://innovation123.tistory.com/9#%EC%8B%B1%EA%B8%80%ED%86%A4%EC%9D%98%20%EB%AC%B8%EC%A0%9C%20%ED%95%B4%EA%B2%B0%20-%20statelsess-1</a></p>
<p><a href="https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%ED%85%9C%ED%94%8C%EB%A6%BF-%EB%A9%94%EC%86%8C%EB%93%9CTemplate-Method-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90">https://inpa.tistory.com/entry/GOF-💠-템플릿-메소드Template-Method-패턴-제대로-배워보자</a></p>
<p><a href="https://dev-coco.tistory.com/177">https://dev-coco.tistory.com/177</a></p>
<p><a href="https://appleg1226.tistory.com/category/Study?page=2">https://appleg1226.tistory.com/category/Study?page=2</a></p>
<p><a href="https://blog.naver.com/jvioonpe/220247760303">https://blog.naver.com/jvioonpe/220247760303</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[백엔드에서 자주 사용되는 디자인패턴 -구조 패턴형-]]></title>
            <link>https://velog.io/@path_creator/%EB%B0%B1%EC%97%94%EB%93%9C%EC%97%90%EC%84%9C-%EC%9E%90%EC%A3%BC-%EC%82%AC%EC%9A%A9%EB%90%98%EB%8A%94-%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4-%EA%B5%AC%EC%A1%B0-%ED%8C%A8%ED%84%B4%ED%98%95-</link>
            <guid>https://velog.io/@path_creator/%EB%B0%B1%EC%97%94%EB%93%9C%EC%97%90%EC%84%9C-%EC%9E%90%EC%A3%BC-%EC%82%AC%EC%9A%A9%EB%90%98%EB%8A%94-%EB%94%94%EC%9E%90%EC%9D%B8%ED%8C%A8%ED%84%B4-%EA%B5%AC%EC%A1%B0-%ED%8C%A8%ED%84%B4%ED%98%95-</guid>
            <pubDate>Fri, 09 Aug 2024 04:08:04 GMT</pubDate>
            <description><![CDATA[<h3 id="span-stylebackground-color-rgba1020050005----구조-패턴------span"><span style="background-color: rgba(10,200,500,0.5)">--- <em>구조 패턴</em> ---- </span></h3>
<h4 id="깃허브-링크--httpsgithubcomjinhan-han-jeremyrealdesignpattern">깃허브 링크 : <a href="https://github.com/Jinhan-Han-Jeremy/RealDesignPattern">https://github.com/Jinhan-Han-Jeremy/RealDesignPattern</a></h4>
<h3 id="span-stylebackground-color-rgba1020010005---------데코레이터decorator---------span"><span style="background-color: rgba(10,200,100,0.5)">-------- 데코레이터(Decorator) --------</span></h3>
<p>객체의 결합을 통해 기능을 동적으로 유연하게 확장할 수 있게 하는 패턴</p>
<ul>
<li><strong>데코레이터 패턴을 사용하는 이유</strong></li>
<li><ul>
<li>전략 패턴을 사용하면 알고리즘의 변경에 따라서 코드 변경을 최소화 가능.</li>
</ul>
</li>
<li><ul>
<li>객체 간의 결합도가 낮아져 유지보수와 테스트가 용이함.</li>
</ul>
</li>
</ul>
<p>*<em>자주 사용되는 경우 : *</em> 데이터 압축 라이브러리 (Data Compression Library), 데이터베이스 접근 라이브러리 (Database Access Library), 결제 처리 시스템 (Payment Processing System), 로깅 라이브러리 (Logging Library), UI 테마 변경 (UI Theme Switching), AI 알고리즘 선택 (AI Algorithm Selection), 비밀번호 해싱 (Password Hashing), 라우팅 알고리즘 (Routing Algorithm), 캐싱 전략 (Caching Strategy), 문서 포맷 변환 (Document Format Conversion)</p>
<p><strong>데코레이터(Decorator) 장점</strong></p>
<ul>
<li>데코레이터를 사용하면 서브클래스를 만들때보다 훨씬 더 유연하게 기능을 확장 가능.</li>
<li>객체를 여러 데코레이터로 래핑하여 여러 동작을 결합 가능.</li>
<li>컴파일 타임이 아닌 런타임에 동적으로 기능을 변경 가능.</li>
<li>구현체가 아닌 인터페이스를 바라봄으로써 의존 역전 원칙(DIP)Visit Website 준수</li>
<li>각 장식자 클래스마다 고유의 책임을 가져 단일 책임 원칙(SRP)Visit Website을 준수</li>
<li>기능 확장이 필요하면 장식자 클래스를 추가하면 되니 개방 폐쇄 원칙(OCP)Visit Website을 준수</li>
<li>구현체가 아닌 인터페이스를 바라봄으로써 의존 역전 원칙(DIP)Visit Website 준수
 </li>
<li><em>데코레이터(Decorator) 단점*</em></li>
<li>만일 장식자 일부를 제거하고 싶다면, Wrapper 스택에서 특정 wrapper를 제거하는 것은 어려움.</li>
<li>데코레이터를 조합하는 초기 생성코드가 보기 안좋을 수 있음. new A(new B(new C(new D())))</li>
<li>어느 장식자를 먼저 데코레이팅 하느냐에 따라 데코레이터 스택 순서가 결정지게 되는데, 만일 순서에 의존하지 않는 방식으로 데코레이터를 구현하기는 어려움.</li>
</ul>
<blockquote>
<p>데코레이터 패턴 예제) <strong>작업 진행 기록을 관리하는 인터페이스와 오브젝트 구성</strong></p>
</blockquote>
<ol>
<li><strong>public interface TaskService { :</strong> 서비스 기능들을 하는 함수들 인터페이스로 구성</li>
<li><strong>public abstract class TaskServiceDecorator implements TaskService { :</strong> TaskService를 상속받고 추상 클래스로 선어</li>
<li><strong>public TaskServiceDecorator(TaskService decoratedTaskService) { :</strong> 생성자 생성 및 인터페이스를 활용할 오브젝트에 값 할당</li>
<li><strong>@Override public void assignTask(String task, String assignee) { :</strong> 서비스 기능들을 상속하고 호출하는 구조의 함수<pre><code class="language-java">package Decorator;
// TaskService 인터페이스: 작업 관리 서비스의 공통 인터페이스를 정의합니다.
public interface TaskService {
 void assignTask(String task, String assignee);
 void updateTaskStatus(String task, String status);
}
package Decorator;
// TaskServiceDecorator 추상 클래스: 작업 관리 서비스 데코레이터의 기본 구조를 정의합니다.
public abstract class TaskServiceDecorator implements TaskService {
 protected TaskService decoratedTaskService;
 public TaskServiceDecorator(TaskService decoratedTaskService) {
     this.decoratedTaskService = decoratedTaskService;
 }
 @Override
 public void assignTask(String task, String assignee) {
     decoratedTaskService.assignTask(task, assignee);
 }
 @Override
 public void updateTaskStatus(String task, String status) {
     decoratedTaskService.updateTaskStatus(task, status);
 }
}</code></pre>
</li>
</ol>
<blockquote>
<p>데코레이터 패턴 예제) <strong>작업 진행 기록들을 하고 이를 추적하는 클래스 구성</strong>
5. <strong>public class BasicTaskService implements TaskService { :</strong> TaskService 인터페이스를 상속하고 서비스가 작동하는 함수들을 구성하는 클래스 생성</p>
</blockquote>
<pre><code class="language-java">package Decorator;
// BasicTaskService 클래스: 기본 작업 관리 서비스를 구현합니다.
public class BasicTaskService implements TaskService {
    @Override
    public void assignTask(String task, String assignee) {
        // 실제 작업 할당 로직
        System.out.println(&quot;Task &#39;&quot; + task + &quot;&#39; assigned to &quot; + assignee);
    }
    @Override
    public void updateTaskStatus(String task, String status) {
        // 실제 작업 상태 업데이트 로직
        System.out.println(&quot;Task &#39;&quot; + task + &quot;&#39; status updated to &quot; + status);
    }
}</code></pre>
<ol start="6">
<li><strong>public class LoggingDecorator extends TaskServiceDecorator { :</strong> TaskServiceDecorator 클래스를 상속하고 따로 로그들을 남기는 기능들을 구성하는 클래스 생성<pre><code class="language-java">package Decorator;
// LoggingDecorator 클래스: 작업 관리 서비스에 로깅 기능을 추가하는 데코레이터를 구현합니다.
public class LoggingDecorator extends TaskServiceDecorator {
 public LoggingDecorator(TaskService decoratedTaskService) {
     super(decoratedTaskService);
 }
 @Override
 public void assignTask(String task, String assignee) {
     System.out.println(&quot;Logging: Assigning task &#39;&quot; + task + &quot;&#39; to &quot; + assignee);
     super.assignTask(task, assignee);
     System.out.println(&quot;Logging: Task &#39;&quot; + task + &quot;&#39; assigned to &quot; + assignee);
 }
 @Override
 public void updateTaskStatus(String task, String status) {
     System.out.println(&quot;Logging: Updating task &#39;&quot; + task + &quot;&#39; status to &quot; + status);
     super.updateTaskStatus(task, status);
     System.out.println(&quot;Logging: Task &#39;&quot; + task + &quot;&#39; status updated to &quot; + status);
 }
}</code></pre>
</li>
<li><strong>public class NotificationDecorator extends TaskServiceDecorator { :</strong> TaskServiceDecorator 클래스를 상속하고 할당되거나 업데이트 된 작업을 알리는 기능들로 구성된 클래스 생성</li>
<li>package Decorator;
// NotificationDecorator 클래스: 작업 상태 업데이트 시 알림 기능을 추가하는 데코레이터를 구현합니다.</li>
<li><strong>@Override public void assignTask(String task, String assignee) { :</strong> 작업 기능들을 상속 후, 알람을 울리는 기능들을 구성</li>
<li><strong>private void notifyAssignee(String task, String message) { :</strong> 알람시에 구성하는 메세지 포맷을 구성<pre><code class="language-java">public class NotificationDecorator extends TaskServiceDecorator {
public NotificationDecorator(TaskService decoratedTaskService) {
    super(decoratedTaskService);
}
@Override
public void assignTask(String task, String assignee) {
    super.assignTask(task, assignee);
    notifyAssignee(task, assignee);
}
@Override
public void updateTaskStatus(String task, String status) {
    super.updateTaskStatus(task, status);
    notifyAssignee(task, status);
}
private void notifyAssignee(String task, String message) {
    // 간단한 알림 로직 (예: 콘솔 출력)
    System.out.println(&quot;Notification: Task &#39;&quot; + task + &quot;&#39; - &quot; + message);
}
}</code></pre>
</li>
</ol>
<blockquote>
<p>데코레이터 패턴 예제) <strong>모든기능들을 이행하는 메인 클래스 구성</strong></p>
</blockquote>
<pre><code class="language-java">import Decorator.BasicTaskService;
import Decorator.LoggingDecorator;
import Decorator.NotificationDecorator;
import Decorator.TaskService;
public class MainByDecorator {
    public static void main(String[] args) {
        TaskService taskService = new BasicTaskService();
        // 작업 관리 서비스에 로깅 데코레이터 추가
        taskService = new LoggingDecorator(taskService);
        // 작업 관리 서비스에 상태 알림 데코레이터 추가
        taskService = new NotificationDecorator(taskService);
        // 작업 할당
        taskService.assignTask(&quot;Design Database Schema&quot;, &quot;Alice&quot;);
        System.out.println();
        // 작업 상태 업데이트
        taskService.updateTaskStatus(&quot;Design Database Schema&quot;, &quot;In Progress&quot;);
    }
}</code></pre>
<p>*<em>전체적 구조  : *</em></p>
<ul>
<li>데코레이터 클래스 { 상태없는 기본 생성자, 단일 인스턴스를 생성하고 사용하는 메서드, 서비스 메소드}
<br/><br/></li>
</ul>
<h3 id="span-stylebackground-color-rgba1020010005---------프록시proxy---------span"><span style="background-color: rgba(10,200,100,0.5)">-------- 프록시(Proxy) --------</span></h3>
<p>특정 객체를 직접 참조하지 않고 해당 객체를 대행(프락시)하는 객체를 통해 접근하는 패턴</p>
<ul>
<li><strong>프록시 패턴을 사용하는 이유</strong></li>
<li><ul>
<li>초기화 지연, 접근 제어, 로깅, 캐싱 등, 기존 객체 동작에 수정 없이 가미하고 싶을 때 </li>
</ul>
</li>
<li><ul>
<li>접근을 제어하거가 기능을 추가하고 싶은데, 기존의 특정 객체를 수정할 수 없는 상황일때</li>
</ul>
</li>
<li><ul>
<li>객체지향 5원칙 중 하나인 OCP를 지키기 원할때</li>
</ul>
</li>
<li><ul>
<li>객체지향 5원칙 중 하나인 SRP를 지키기 원할때</li>
</ul>
</li>
<li><ul>
<li>유연한 코드 개발 가능</li>
</ul>
</li>
</ul>
<p><strong>자주 사용되는 경우</strong> : 지연 로딩 (Lazy Loading), 액세스 제어 (Access Control), 원격 프록시 (Remote Proxy), 캐싱 (Caching), 로깅 및 모니터링 (Logging and Monitoring), 트랜잭션 관리 (Transaction Management), 외부 서비스 연결 (External Service Connection), 원격 서비스 접근 (Remote Service Access)</p>
<p><strong>프록시(Proxy) 장점</strong></p>
<ul>
<li>개방 폐쇄 원칙(OCP)Visit Website 준수</li>
<li><ul>
<li>기존 대상 객체의 코드를 변경하지 않고 새로운 기능을 추가 가능.</li>
</ul>
</li>
<li>단일 책임 원칙(SRP)Visit Website 준수 </li>
<li><ul>
<li>대상 객체는 자신의 기능에만 집중 하고, 그 이외 부가 기능을 제공하는 역할을 프록시 객체에 위임하여 다중 책임을 회피 가능.</li>
</ul>
</li>
<li>원래 하려던 기능을 수행하며 그외의 부가적인 작업(로깅, 인증, 네트워크 통신 등)을 수행하는데 유용</li>
<li>클라이언트는 객체를 신경쓰지 않고, 서비스 객체를 제어하거나 생명 주기를 관리 가능.</li>
<li>프록시 객체는 실제 객체처럼 사용이 편함.</li>
</ul>
<p><strong>프록시(Proxy) 단점</strong></p>
<ul>
<li>많은 프록시 클래스를 도입해야 하므로 코드의 복잡도가 증가.</li>
<li><ul>
<li>예를들어 여러 클래스에 로깅 기능을 가미 시키고 싶다면, 동일한 코드를 적용함에도 각각의 클래스에 해당되는 프록시 클래스를 만들어서 적용해야 되기 때문에 코드량이 많아지고 중복이 발생.</li>
</ul>
</li>
<li><ul>
<li>자바에서는 리플렉션에서 제공하는 동적 프록시(Dynamic Proxy) 기법을 이용해서 해결 가능. (후술)</li>
</ul>
</li>
<li>프록시 클래스 자체에 들어가는 자원이 많다면 서비스로부터의 응답이 늦어질 수 있음.</li>
</ul>
<p>프록시 종류: 기본형, 가상형, 보호, 로깅, 원격, 캐싱
<a href="https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%ED%94%84%EB%A1%9D%EC%8B%9CProxy-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90">https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%ED%94%84%EB%A1%9D%EC%8B%9CProxy-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90</a></p>
<p><strong>캐싱 프록시</strong></p>
<ul>
<li>데이터가 큰 경우 캐싱하여 재사용을 유도</li>
<li>클라이언트 요청의 결과를 캐시하고, 캐시의 주기 관리</li>
</ul>
<blockquote>
<p>캐싱 프록시 예제) <strong>캐싱 프록시로 데이터를 캐싱에저장</strong>
1.<strong>public interface DatabaseService {:</strong> 쿼리를 캐시에 저장하는 인터페이스 사용. </p>
</blockquote>
<pre><code class="language-java">package CachingProxy;
public interface DatabaseService {
    String queryDatabase(String query);
}</code></pre>
<p>2.<strong>class RealDatabaseService implements DatabaseService{ :</strong> 데이터베이스 서비스 기능을 하는 클래스 구성.
3.<strong>public String queryDatabase(String query) { :</strong> 데이터베이스 서비스 기능을 하는 클래스 구성이며 지연시뮬레이션을 넣어 캐싱의 성능 테스트가 용이해짐.</p>
<pre><code class="language-java">package CachingProxy;
class RealDatabaseService implements DatabaseService{
    @Override
    public String queryDatabase(String query) {
        // Simulate a costly database query
        try {
            Thread.sleep(3000); // Simulate delay
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return &quot;Result for query: &quot; + query;
    }
}</code></pre>
<blockquote>
<p>4.<strong>public class CachingDatabaseProxy implements DatabaseService{ :</strong> 데이터베이스 서비스 기능을 하는 클래스 구성이며 지연시뮬레이션을 넣어 캐싱의 성능 테스트가 용이해짐.
5.<strong>String result = realService.queryDatabase(query); :</strong> 지연시뮬레이션을 시행하여 캐시에 데이터를 삽입.</p>
</blockquote>
<pre><code class="language-java">package CachingProxy;
import java.util.HashMap;
import java.util.Map;
public class CachingDatabaseProxy implements DatabaseService{
    private RealDatabaseService realService = new RealDatabaseService();
    private Map&lt;String, String&gt; cache = new HashMap&lt;&gt;();
    @Override
    public String queryDatabase(String query) {
        if (!cache.containsKey(query)){
            // 실제 서비스에서 쿼리 결과를 가져와 캐시에 저장
            String result = realService.queryDatabase(query);
            cache.put(query, result);
        }
        else{
            // 캐시에 저장된 결과 반환
            System.out.println(&quot;Returning cached result for query: &quot; + query);
        }
        return cache.get(query);
    }
}</code></pre>
<blockquote>
<p>캐싱 패턴 예제) <strong>모든 기능들을 이행하는 메인 클래스 구성</strong></p>
</blockquote>
<pre><code class="language-java">import CachingProxy.CachingDatabaseProxy;
import CachingProxy.DatabaseService;
public class MainByCachingProxy {
    public static void main(String[] args) {
        DatabaseService service = new CachingDatabaseProxy();
        // First call - result is not cached
        System.out.println(service.queryDatabase(&quot;SELECT * FROM users&quot;));
        // Second call - result should be cached
        System.out.println(service.queryDatabase(&quot;SELECT name FROM users&quot;));
        // Third call - different query, result is not cached
        System.out.println(service.queryDatabase(&quot;SELECT type FROM orders&quot;));
    }
}</code></pre>
<p>*<em>전체적 구조  : *</em></p>
<ul>
<li>인터페이스{서비스 함수 호출}</li>
<li>지연 시뮬레이션 클래스 implements 인터페이스{@OVerride 서비스 함수}</li>
<li>캐싱 프록시 클래스 implements 인터페이스{@OVerride 서비스 함수{지연시뮬레이션 서비스 함수 활용}}
<br/><br/></li>
</ul>
<p><strong>원격 프록시</strong></p>
<ul>
<li>프록시 클래스는 로컬에 있고, 대상 객체는 원격 서버에 존재하는 경우</li>
<li>프록시 객체는 네트워크를 통해 클라이언트의 요청을 전달하여 네트워크와 관련된 불필요한 작업들을 처리하고 결과값만 반환</li>
</ul>
<blockquote>
<p>원격 프록시 예제) <strong>원격 프록시로 데이터를 패칭</strong>
1.<strong>public interface DatabaseService {:</strong> 쿼리를 캐시에 저장하는 인터페이스 사용. </p>
</blockquote>
<pre><code class="language-java">package RemoteProxy;
import java.rmi.Remote;
import java.rmi.RemoteException;
// Remote Interface
public interface RemoteService extends Remote {
    String fetchData(String param) throws RemoteException;
}</code></pre>
<p>2.<strong>public class RealRemoteService extends UnicastRemoteObject implements RemoteService { :</strong> 리모트서비스 인터페이스 상속 및 UnicastRemoteObject 상속.
3.<strong>@Override public String fetchData(String param) throws RemoteException {:</strong> fetchData의 기능을 Override 및 활용.</p>
<pre><code class="language-java">package RemoteProxy;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
// RealSubject
public class RealRemoteService extends UnicastRemoteObject implements RemoteService {
    protected RealRemoteService() throws RemoteException {
        super();
    }
    @Override
    public String fetchData(String param) throws RemoteException {
        // Simulate fetching data from a remote server
        return &quot;Data from server for &quot; + param;
    }
}</code></pre>
<blockquote>
<p>4.<strong>public class RemoteServiceProxy implements RemoteService { :</strong> RemoteService를 상속 및 생성 자와 서비스 기능 추가.
5.<strong>@Override public String fetchData(String param) throws RemoteException { :</strong> fetchData로 재귀 형태로 서비스 기능을 활용</p>
</blockquote>
<pre><code class="language-java">package RemoteProxy;
import java.rmi.RemoteException;
// Proxy
public class RemoteServiceProxy implements RemoteService {
    private final RemoteService realService;
    public RemoteServiceProxy(RemoteService realService) {
        this.realService = realService;
    }
    @Override
    public String fetchData(String param) throws RemoteException {
        System.out.println(&quot;Proxy: Fetching data for &quot; + param);
        return realService.fetchData(param);
    }
}</code></pre>
<blockquote>
<p>원격 프록시 예제) <strong>서버 기능을 실행하여, 원격이 가능하게함</strong> </p>
</blockquote>
<pre><code class="language-java">package RemoteProxy;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
// Server
public class Server {
    public static void main(String[] args) {
        try {
            RealRemoteService realService = new RealRemoteService();
            Registry registry = LocateRegistry.createRegistry(1099);
            registry.bind(&quot;RemoteService&quot;, realService);
            System.out.println(&quot;Server started&quot;);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}</code></pre>
<blockquote>
<p>원격 프록시 예제) <strong>모든 기능들을 이행하는 메인 클래스 구성</strong> </p>
</blockquote>
<pre><code class="language-java">import RemoteProxy.RemoteService;
import RemoteProxy.RemoteServiceProxy;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
public class MainByRemoteProxy {
    public static void main(String[] args) {
        try {
            Registry registry = LocateRegistry.getRegistry(&quot;localhost&quot;, 1099);
            RemoteService realService = (RemoteService) registry.lookup(&quot;RemoteService&quot;);
            RemoteServiceProxy proxy = new RemoteServiceProxy(realService);
            // Fetch data through the proxy         System.out.println(proxy.fetchData(&quot;test1&quot;));
            System.out.println(proxy.fetchData(&quot;test2&quot;));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}</code></pre>
<p>*<em>전체적 구조  : *</em></p>
<ul>
<li>인터페이스{서비스 함수 호출}</li>
<li>원격 기능 클래스 implements 인터페이스{기본 생성자, @OVerride 서비스 함수}</li>
<li>원격 프록시 클래스 implements 인터페이스{생성자(변수들), @OVerride 서비스 함수 throws RemoteException{서비스 기능들}}</li>
<li>서버 클래스{ 원격 기능 서비스들 호출}
<br/><br/></li>
</ul>
<h3 id="span-stylebackground-color-rgba1020010005---------퍼사드facade---------span"><span style="background-color: rgba(10,200,100,0.5)">-------- 퍼사드(Facade) --------</span></h3>
<p>서브시스템의 인터페이스 집합들에 하나의 통합된 인터페이스를 제공하는 패턴</p>
<ul>
<li><strong>퍼사드 패턴을 사용하는 이유</strong></li>
<li><ul>
<li>복잡한 시스템의 간단한 인터페이스가 필요한 경우</li>
</ul>
</li>
<li><ul>
<li>간단한 인터페이스를 통해 복잡한 시스템을 접근할때, 시스템의 결합도를 줄이고 유연성을 높이는 경우</li>
</ul>
</li>
<li><ul>
<li>서브 시스템을 노출하지 않고 사용자 인터페이스를 제공하는 경우</li>
</ul>
</li>
<li><ul>
<li>시스템을 사용하고 있는 외부와 결합도가 너무 높을 때 의존성 낮추기 위할때</li>
</ul>
</li>
</ul>
<p><strong>자주 사용되는 경우</strong> : 서브시스템의 단순화 (Simplification of Subsystems), 외부 API와의 통합 (Integration with External APIs), 크로스커팅 관심사 처리 (Handling Cross-Cutting Concerns), 레거시 코드와의 통합 (Integration with Legacy Code), 테스트 용이성 향상 (Improving Testability), 사용자 인증 (User Authentication), 결제 시스템 (Payment System)</p>
<p><strong>퍼사드(Facade) 장점</strong></p>
<ul>
<li>하위 시스템의 복잡성에서 코드를 분리하여, 외부에서 시스템을 사용하기 쉬움.</li>
<li>하위 시스템 간의 의존 관계가 많을 경우 이를 감소시키고 의존성을 한 곳으로 모을 수 있음.</li>
<li>복잡한 코드를 감춤으로써, 클라이언트가 시스템의 코드를 모르더라도 Facade 클래스만 이해하고 사용 가능. </li>
</ul>
<p><strong>퍼사드(Facade) 단점</strong></p>
<ul>
<li>퍼사드가 앱의 모든 클래스에 결합된 God 객체가 될 수 있음.</li>
<li>퍼사드 클래스 자체가 서브시스템에 대한 의존성을 가지게 되어 의존성을 완전히는 피하는건 불가능.</li>
<li>어찌되었건 추가적인 코드가 늘어나는 것이기 때문에 유지보수 측면에서 공수가 더 많이 들게 됨.</li>
<li>따라서 추상화 하고자하는 시스템이 얼마나 복잡한지 퍼사드 패턴을 통해서 얻게 되는 이점과 추가적인 유지보수 비용을 비교해보며 결정해야 함.</li>
</ul>
<blockquote>
<p>퍼사드 예제) <strong>DBMS 시스템 재구성 프로그램</strong>
퍼사드 예제) <strong>클래스들의 구성 Row,Cache,DBMS,Message</strong></p>
</blockquote>
<ol>
<li><strong>class Row  { :</strong> 데이터에 저장되는 형식 정보<pre><code class="language-java">package Facade;
class Row  {
 private String name;
 private String birthday;
 private String email;
 public Row(String name, String birthday, String email) {
     this.name = name;
     this.birthday = birthday;
     this.email = email;
 }
 public String getName() {
     return name;
 }
 public String getBirthday() {
     return birthday;
 }
 public String getEmail() {
     return email;
 }
}</code></pre>
</li>
<li><strong>class DBMS { :</strong> 데이터베이스 저장 및 호출.<pre><code class="language-java">package Facade;
import java.util.HashMap;
// 데이터베이스 역할을 하는 클래스
class DBMS {
 private HashMap&lt;String, Row&gt; db = new HashMap&lt;&gt;();
 public void put(String name, Row row) {
     db.put(name, row);
 }
 // 데이터베이스에 쿼리를 날려 결과를 받아오는 메소드
 public Row query(String name) {
     try {
         Thread.sleep(500); // DB 조회 시간을 비유하여 0.5초대기로 구현
     } catch (InterruptedException e) {
     }
     return db.get(name.toLowerCase());
 }
}</code></pre>
</li>
<li><strong>class Cache { :</strong> 캐시에 저장 및 호출.<pre><code class="language-java">package Facade;
import java.util.HashMap;
// 데이터베이스 역할을 하는 클래스
class DBMS {
 private HashMap&lt;String, Row&gt; db = new HashMap&lt;&gt;();
 public void put(String name, Row row) {
     db.put(name, row);
 }
 // 데이터베이스에 쿼리를 날려 결과를 받아오는 메소드
 public Row query(String name) {
     try {
         Thread.sleep(500); // DB 조회 시간을 비유하여 0.5초대기로 구현
     } catch (InterruptedException e) {
     }
     return db.get(name.toLowerCase());
 }
}</code></pre>
</li>
<li><strong>class Message { :</strong> 데이터를 출력하는 구성.<pre><code class="language-java">package Facade;
// Row 클래스를 보기좋게 출력하는 클래스
class Message {
 private Row row;
 public Message(Row row) {
     this.row = row;
 }
 public String makeName() {
     return &quot;Name : \&quot;&quot; + row.getName() + &quot;\&quot;&quot;;
 }
 public String makeBirthday() {
     return &quot;Birthday : &quot; + row.getBirthday();
 }
 public String makeEmail() {
     return &quot;Email : &quot; + row.getEmail();
 }
}</code></pre>
</li>
</ol>
<blockquote>
<ol start="5">
<li><strong>public class Facade { :</strong> 파사드 패턴으로 클래스들 호출 구성.</li>
<li><strong>public void insert() { :</strong> 데이터 입력.</li>
<li><strong>public void run(String name) { :</strong> 데이터 결과 출력.</li>
</ol>
</blockquote>
<pre><code class="language-java">package Facade;
public class Facade {
    private DBMS dbms = new DBMS();
    private Cache cache = new Cache();
    public void insert() {
        dbms.put(&quot;홍길동&quot;, new Row(&quot;홍길동&quot;, &quot;1890-02-14&quot;, &quot;honggildong@naver.com&quot;));
        dbms.put(&quot;임꺽정&quot;, new Row(&quot;임꺽정&quot;, &quot;1820-11-02&quot;, &quot;imgguckjong@naver.com&quot;));
        dbms.put(&quot;주몽&quot;, new Row(&quot;주몽&quot;, &quot;710-08-27&quot;, &quot;jumong@naver.com&quot;));
    }
    public void run(String name) {
        Row row = cache.get(name);
        // 1. 만약 캐시에 없다면
        if (row == null){
            row = dbms.query(name); // DB에 해당 데이터를 조회해서 row에 저장하고
            if(row != null) {
                cache.put(row); // 캐시에 저장
            }
        }
        // 2. dbms.query(name)에서 조회된 값이 있으면
        if(row != null) {
            Message message = new Message(row);
            System.out.println(message.makeName());
             System.out.println(message.makeBirthday());
             System.out.println(message.makeEmail());
        }
        // 3. 조회된 값이 없으면
        else {
            System.out.println(name + &quot; 가 데이터베이스에 존재하지 않습니다.&quot;);
        }
    }
}</code></pre>
<blockquote>
<p>파사드 패턴 예제) <strong>모든 기능들을 이행하는 메인 클래스 구성</strong> </p>
</blockquote>
<pre><code class="language-java">import Facade.*;
public class MainByFacade {
    public static void main(String[] args) {
        // 1. 퍼사드 객체 생성
        Facade facade = new Facade();
        // 2. db 값 insert
        facade.insert();
        // 3. 퍼사드로 데이터베이스 &amp; 캐싱 &amp; 메세징 로직을 한번에 조회
        String name = &quot;홍길동&quot;;
        facade.run(name);
    }
}</code></pre>
<p>*<em>전체적 구조  : *</em></p>
<ul>
<li>구성 클래스들 (상황마다 구성 클래스들 마다 다름)</li>
<li>퍼사드 클래스 { 필요 객체들 호출, 필요 기능들을 구성}
<br/><br/></li>
</ul>
<p>참조 :
<a href="https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EC%B6%94%EC%83%81-%ED%8C%A9%ED%86%A0%EB%A6%ACAbstract-Factory-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90#%EC%B6%94%EC%83%81_%ED%8C%A9%ED%86%A0%EB%A6%AC_%ED%8C%A8%ED%84%B4_%ED%8A%B9%EC%A7%95">https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EC%B6%94%EC%83%81-%ED%8C%A9%ED%86%A0%EB%A6%ACAbstract-Factory-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90#%EC%B6%94%EC%83%81_%ED%8C%A9%ED%86%A0%EB%A6%AC_%ED%8C%A8%ED%84%B4_%ED%8A%B9%EC%A7%95</a></p>
<p><a href="https://innovation123.tistory.com/9#%EC%8B%B1%EA%B8%80%ED%86%A4%EC%9D%98%20%EB%AC%B8%EC%A0%9C%20%ED%95%B4%EA%B2%B0%20-%20statelsess-1">https://innovation123.tistory.com/9#%EC%8B%B1%EA%B8%80%ED%86%A4%EC%9D%98%20%EB%AC%B8%EC%A0%9C%20%ED%95%B4%EA%B2%B0%20-%20statelsess-1</a></p>
<p><a href="https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%ED%85%9C%ED%94%8C%EB%A6%BF-%EB%A9%94%EC%86%8C%EB%93%9CTemplate-Method-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90">https://inpa.tistory.com/entry/GOF-💠-템플릿-메소드Template-Method-패턴-제대로-배워보자</a></p>
<p><a href="https://dev-coco.tistory.com/177">https://dev-coco.tistory.com/177</a></p>
<p><a href="https://appleg1226.tistory.com/category/Study?page=2">https://appleg1226.tistory.com/category/Study?page=2</a></p>
<p><a href="https://blog.naver.com/jvioonpe/220247760303">https://blog.naver.com/jvioonpe/220247760303</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바 버전 문제로 생기는 오류 해결]]></title>
            <link>https://velog.io/@path_creator/%EC%9E%90%EB%B0%94-%EB%B2%84%EC%A0%84-%EB%AC%B8%EC%A0%9C%EB%A1%9C-%EC%83%9D%EA%B8%B0%EB%8A%94-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0</link>
            <guid>https://velog.io/@path_creator/%EC%9E%90%EB%B0%94-%EB%B2%84%EC%A0%84-%EB%AC%B8%EC%A0%9C%EB%A1%9C-%EC%83%9D%EA%B8%B0%EB%8A%94-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0</guid>
            <pubDate>Wed, 17 Jul 2024 02:44:04 GMT</pubDate>
            <description><![CDATA[<h1 id="case-1-자바-환경-변수-재설정">case 1. 자바 환경 변수 재설정</h1>
<h3 id="1-작업표시줄-검색창에-환경-변수를-검색하여-시스템-환경-변수-편집으로-들어갑니다">1. 작업표시줄 검색창에 환경 변수를 검색하여 시스템 환경 변수 편집으로 들어갑니다.</h3>
<p><img src="https://velog.velcdn.com/images/path_creator/post/c5cf6e21-3972-421f-9599-a1c0a20d4b91/image.png" alt=""></p>
<h3 id="2-시스템-속성의-고급-탭에서-우측-하단의-환경-변수를-클릭합니다">2. 시스템 속성의 고급 탭에서 우측 하단의 환경 변수를 클릭합니다.</h3>
<p><img src="https://velog.velcdn.com/images/path_creator/post/9abf9c49-bd4b-4b0d-9745-60ef40429891/image.png" alt=""></p>
<h3 id="3-환경-변수에서-시스템-변수-섹션에-새로-만들기-버튼을-클릭하여-아래-정보를-입력하고-확인버튼을-클릭합니다">3. 환경 변수에서 시스템 변수 섹션에 새로 만들기 버튼을 클릭하여 아래 정보를 입력하고 확인버튼을 클릭합니다.</h3>
<p><img src="https://velog.velcdn.com/images/path_creator/post/1d9d3e70-84e6-477e-8254-ce5ab36cce1c/image.png" alt=""></p>
<p><strong>자바 경로는 주로 Program Files에 java 폴더에 존재 아래 그림 참조</strong>
속성을 눌러 경로 복사는 아래처럼 복사하면 된다.
<img src="https://velog.velcdn.com/images/path_creator/post/7d8599b2-e861-479f-8752-a5eaf1283985/image.png" alt=""></p>
<h3 id="4-환경-변수-편집에서-java_homebin-가장-위로-배치">4. 환경 변수 편집에서 %JAVA_HOME%\bin 가장 위로 배치</h3>
<p><img src="https://velog.velcdn.com/images/path_creator/post/fb505139-251a-4367-badd-0f2ba84f746b/image.png" alt=""></p>
<p><strong>%JAVA_HOME%\bin을 가장 위로 올려주는 이유</strong> : 명령어를 찾을 때, Path 환경변수에 등록된 순서대로 찾기 때문입니다. 그렇기 때문에 %JAVA_HOME%\bin보다 먼저 등록된 폴더에 java 명령어가 있다면 다른 버전의 java 명령어가 사용될 수 있고, 새로 설치한 java가 제대로 인식이 안되는 경우가 발생할 수 있기 때문에 가장 위로 올려줍니다.</p>
<p>또는 충돌 예방을 위하여 다른** JAVA_HOME**과 같은 자바를 삭제를 하여도 됩니다.</p>
<p>변수 이름(N) : <strong>JAVA_HOME</strong></p>
<p>변수 값(V) : JDK가 설치된 폴더의 경로</p>
<p>저의 경우에는 자바 17 버전을 다운받아 C:\Program Files\Java\jdk-17 해당 값을 입력해 주었습니다.</p>
<p><strong>jdk-17(자바 17버전)보다 더 높은 수준의 버전 요구시에
자바 신규 버전으로 재다운로드 후, JAVA_HOME 변수 폴더 경로 신버전으로 편집</strong></p>
<blockquote>
<p>컴파일러의 자바 설정 문제로 코드가 실행되지 않는 경우도 있음. 
신규 버젼으로 다운시 그 아래 구형 버젼 코드 역시 자유롭게 사용 가능.</p>
</blockquote>
<p>아래는 자바 다운로드 링크 -반드시, jdk로 다운로드
<a href="https://www.oracle.com/kr/java/technologies/downloads/">https://www.oracle.com/kr/java/technologies/downloads/</a></p>
<h3 id="설치가-잘-되었고-잘-적용-되었는지-확인-">설치가 잘 되었고 잘 적용 되었는지 확인 :</h3>
<p>파워쉘에서 자바 버전 확인 만약 잘 적용이 안되었다면 <strong><em>재부팅!!</em></strong>
<img src="https://velog.velcdn.com/images/path_creator/post/4b5a01cd-b528-4822-9ade-871c76a2f865/image.png" alt=""></p>
<h2 id="추가적으로-maven이나-gradle-설정에-관한-자료">추가적으로 maven이나 gradle 설정에 관한 자료</h2>
<p><a href="https://velog.io/@bi-sz/Gradle-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0-%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98-%EC%84%A4%EC%A0%95">https://velog.io/@bi-sz/Gradle-%EC%84%A4%EC%B9%98%ED%95%98%EA%B8%B0-%ED%99%98%EA%B2%BD%EB%B3%80%EC%88%98-%EC%84%A4%EC%A0%95</a></p>
<p><a href="https://devpad.tistory.com/19">https://devpad.tistory.com/19</a></p>
<h1 id="case-2-인텔리제이-자바-버전-설정-문제">case 2. 인텔리제이 자바 버전 설정 문제</h1>
<h3 id="1-intellij에서-project-structure-선택하여-프로젝트에서-sdk-버젼-설정">1. IntelliJ에서 Project Structure 선택하여 프로젝트에서 SDK 버젼 설정</h3>
<p><img src="https://velog.velcdn.com/images/path_creator/post/56a2a08e-8b8f-43df-af05-18d54f1e66f2/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/path_creator/post/c44eac4d-940c-42b3-b5f5-cb723aad7812/image.png" alt=""></p>
<h3 id="2-buildgradle에서-sourcecompability--버전에-맞게-설정">2. Build.gradle에서 sourceCompability = &#39;버전&#39;에 맞게 설정</h3>
<p><img src="https://velog.velcdn.com/images/path_creator/post/d68d509f-fdbb-4e93-bde2-fc84cdc1fde9/image.png" alt="">
아래처럼 설정</p>
<pre><code class="language-shell">sourceCompatibility = &#39;17&#39;</code></pre>
<p>또는</p>
<pre><code class="language-shell">java {
    sourceCompatibility = &#39;17&#39;
}</code></pre>
<h3 id="3-setting에서-buildexecution-build-tools아래에-gradle에서-gradle-jvm-버전-설정">3. setting에서 Build,Execution.. Build Tools아래에 Gradle에서 Gradle JVM 버전 설정</h3>
<p>파일 아래 settiing
<img src="https://velog.velcdn.com/images/path_creator/post/891808e1-cfcd-443e-a382-8772cce9bacf/image.png" alt="">
Build,Execution.. -&gt; Build Tools아래에 Gradle -&gt; Gradle JVM 버전 설정 </p>
<p>Build and run과 Run tests using을 Intellij IDEA로 설정하면 속도가 빨라짐
<img src="https://velog.velcdn.com/images/path_creator/post/0906ff77-277f-4876-b161-2b661f1656dd/image.png" alt=""></p>
<h4 id="만약-이래도-자바-버젼등으로-문제가-생긴다면">만약 이래도 자바 버젼등으로 문제가 생긴다면?</h4>
<p><img src="https://velog.velcdn.com/images/path_creator/post/e1b8633f-beca-47d2-aad2-077ae0572597/image.png" alt=""></p>
<p>추천하진 않지만 build.gradle에서 <strong>자바 버젼 자체를 지우고</strong> 개발을 하고 그 이후 해결방법을 알아보는것을 권장한다.</p>
<h3 id="부록-추가-참고-사항-intelij-에서-추가적으로-추천되는-설정-한글-깨짐-예방--import-구성-최적화-등등">부록) 추가 참고 사항 intelij 에서 추가적으로 추천되는 설정 (한글 깨짐 예방 + import 구성 최적화 등등)</h3>
<p><img src="https://velog.velcdn.com/images/path_creator/post/8e5fc4ae-9cc4-435b-9503-c28ffd967ca1/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/path_creator/post/3a0e13f5-920c-4598-a687-e4cd347592c0/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/path_creator/post/d8d99b9e-15bc-4006-8c3e-899031cc2000/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/path_creator/post/5bbf3cbc-f86b-43b8-be98-3c04adb2084f/image.png" alt=""></p>
<p>참조: 
<a href="https://velog.io/@bi-sz/Java-%ED%99%98%EA%B2%BD-%EB%B3%80%EC%88%98-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0">https://velog.io/@bi-sz/Java-%ED%99%98%EA%B2%BD-%EB%B3%80%EC%88%98-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0</a></p>
<p><a href="https://coding-factory.tistory.com/838">https://coding-factory.tistory.com/838</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[서비스 개발에 필요한 JPA의 CrudRepository 기능들]]></title>
            <link>https://velog.io/@path_creator/Temp-Title-zwj1p77t</link>
            <guid>https://velog.io/@path_creator/Temp-Title-zwj1p77t</guid>
            <pubDate>Mon, 01 Jul 2024 02:33:39 GMT</pubDate>
            <description><![CDATA[<h1 id="spring-data-jpa의-crudrepository-사용법">Spring Data JPA의 CrudRepository 사용법</h1>
<p>CrudRepository 인터페이스는 Spring Data JPA에서 제공하는 기본적인 CRUD(Create, Read, Update, Delete) 작업을 위한 메서드들을 정의합니다.
이 인터페이스는 주로 spring boot api의 service 기능에서 다양한 CRUD 기능을 개발시에 사용 합니다. 이번 포스팅에서는 CrudRepository가 제공하는 메서드들과 그 사용 예제에 대해 설명하겠습니다.</p>
<h2 id="crudrepository-인터페이스-메서드-목록">CrudRepository 인터페이스 메서드 목록</h2>
<h3 id="saves-entity"><strong>save(S entity)</strong></h3>
<p>주어진 엔티티를 저장하거나 업데이트합니다.</p>
<p>예제:</p>
<pre><code class="language-java">User user = new User();
user.setName(&quot;John Doe&quot;);
user.setEmail(&quot;john.doe@example.com&quot;);
userRepository.save(user);</code></pre>
<h3 id="savealliterable-s-entities">saveAll(Iterable &lt;S&gt; entities)</h3>
<p>주어진 엔티티들의 컬렉션을 모두 저장하거나 업데이트합니다.</p>
<p>예제:</p>
<pre><code class="language-java">List&lt;User&gt; users = Arrays.asList(
    new User(&quot;Alice&quot;, &quot;alice@example.com&quot;),
    new User(&quot;Bob&quot;, &quot;bob@example.com&quot;)
);
userRepository.saveAll(users);</code></pre>
<h3 id="findbyidid-id">findById(ID id)</h3>
<p>주어진 ID를 가진 엔티티를 반환합니다. 존재하지 않으면 Optional.empty()를 반환합니다.</p>
<p>예제:</p>
<pre><code class="language-java">Optional&lt;User&gt; user = userRepository.findById(1L);
user.ifPresent(u -&gt; System.out.println(u.getName()));</code></pre>
<h3 id="existsbyidid-id">existsById(ID id)</h3>
<p>주어진 ID를 가진 엔티티가 존재하는지 여부를 반환합니다.</p>
<p>예제:</p>
<pre><code class="language-java">boolean exists = userRepository.existsById(1L);
System.out.println(&quot;User exists: &quot; + exists);</code></pre>
<h3 id="findall">findAll()</h3>
<p>모든 엔티티를 반환합니다.</p>
<p>예제:</p>
<pre><code class="language-java">Iterable&lt;User&gt; users = userRepository.findAll();
users.forEach(user -&gt; System.out.println(user.getName()));</code></pre>
<h3 id="findallbyiditerableid-ids">findAllById(Iterable&lt;ID&gt; ids)</h3>
<p>주어진 ID들을 가진 모든 엔티티를 반환합니다.</p>
<p>예제:</p>
<pre><code class="language-java">List&lt;Long&gt; ids = Arrays.asList(1L, 2L, 3L);
Iterable&lt;User&gt; users = userRepository.findAllById(ids);
users.forEach(user -&gt; System.out.println(user.getName()));</code></pre>
<h3 id="count">count()</h3>
<p>총 엔티티의 수를 반환합니다.</p>
<p>예제:</p>
<pre><code class="language-java">long count = userRepository.count();
System.out.println(&quot;Total users: &quot; + count);</code></pre>
<h3 id="deletebyidid-id">deleteById(ID id)</h3>
<p>주어진 ID를 가진 엔티티를 삭제합니다.</p>
<p>예제:</p>
<pre><code class="language-java">userRepository.deleteById(1L);</code></pre>
<h3 id="deletet-entity">delete(T entity)</h3>
<p>주어진 엔티티를 삭제합니다.</p>
<p>예제:</p>
<pre><code class="language-java">User user = userRepository.findById(1L).orElseThrow();
userRepository.delete(user);
deleteAllById(Iterable&lt;? extends ID&gt; ids)</code></pre>
<p>주어진 ID들을 가진 모든 엔티티를 삭제합니다.</p>
<p>예제:</p>
<pre><code class="language-java">List&lt;Long&gt; ids = Arrays.asList(1L, 2L, 3L);
userRepository.deleteAllById(ids);</code></pre>
<h3 id="deletealliterable-extends-t-entities">deleteAll(Iterable&lt;? extends T&gt; entities)</h3>
<p>주어진 엔티티들의 컬렉션을 모두 삭제합니다.</p>
<p>예제:</p>
<pre><code class="language-java">List&lt;User&gt; users = userRepository.findAll();
userRepository.deleteAll(users);</code></pre>
<h3 id="deleteall">deleteAll()</h3>
<p>모든 엔티티를 삭제합니다.</p>
<p>예제:</p>
<pre><code class="language-java">userRepository.deleteAll();</code></pre>
<h4 id="예제-코드-및-설명">예제 코드 및 설명</h4>
<p>User 엔티티 클래스
먼저, User 엔티티 클래스를 정의합니다.</p>
<pre><code class="language-java">import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String email;

    public User() {}

    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }

    // Getters and setters
    // toString method
}
### UserRepository 인터페이스
CrudRepository를 확장하여 UserRepository 인터페이스를 정의합니다.</code></pre>
<pre><code class="language-java">import org.springframework.data.repository.CrudRepository;

public interface UserRepository extends CrudRepository&lt;User, Long&gt; {
}</code></pre>
<h3 id="userservice-클래스">UserService 클래스</h3>
<p>UserService 클래스는 UserRepository를 사용하여 비즈니스 로직을 구현합니다.</p>
<pre><code class="language-java">import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Optional;

@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public User saveUser(User user) {
        return userRepository.save(user);
    }

    public Optional&lt;User&gt; getUserById(Long id) {
        return userRepository.findById(id);
    }

    public Iterable&lt;User&gt; getAllUsers() {
        return userRepository.findAll();
    }

    public void deleteUserById(Long id) {
        userRepository.deleteById(id);
    }
}</code></pre>
<h3 id="usercontroller-클래스">UserController 클래스</h3>
<p>UserController 클래스는 HTTP 요청을 처리하며, 클라이언트와 서버 간의 인터페이스 역할을 합니다.</p>
<pre><code class="language-java">import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping(&quot;/users&quot;)
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping
    public ResponseEntity&lt;User&gt; createUser(@RequestBody User user) {
        User createdUser = userService.saveUser(user);
        return ResponseEntity.ok(createdUser);
    }

    @GetMapping(&quot;/{id}&quot;)
    public ResponseEntity&lt;User&gt; getUserById(@PathVariable Long id) {
        Optional&lt;User&gt; user = userService.getUserById(id);
        return user.map(ResponseEntity::ok)
                   .orElseGet(() -&gt; ResponseEntity.notFound().build());
    }

    @GetMapping
    public ResponseEntity&lt;Iterable&lt;User&gt;&gt; getAllUsers() {
        Iterable&lt;User&gt; users = userService.getAllUsers();
        return ResponseEntity.ok(users);
    }

    @DeleteMapping(&quot;/{id}&quot;)
    public ResponseEntity&lt;Void&gt; deleteUserById(@PathVariable Long id) {
        userService.deleteUserById(id);
        return ResponseEntity.noContent().build();
    }
}</code></pre>
<h2 id="전체-흐름-요약">전체 흐름 요약</h2>
<p><strong>User 엔티티 클래스:</strong> User 엔티티는 데이터베이스 테이블과 매핑됩니다.
<strong>UserRepository 인터페이스:</strong> CrudRepository를 확장하여 기본 CRUD 작업을 위한 메서드를 상속받습니다.
<strong>UserService 클래스:</strong> UserRepository를 사용하여 비즈니스 로직을 처리합니다.
<strong>UserController 클래스:</strong> HTTP 요청을 처리하고, 서비스 계층을 통해 데이터베이스와 상호작용합니다.</p>
<p>이 구조를 통해 User 엔티티의 CRUD 작업을 간단하고 효율적으로 처리할 수 있습니다. CrudRepository가 제공하는 기본 메서드를 사용하여 반복적인 데이터 접근 로직을 줄이고, 비즈니스 로직에 집중할 수 있습니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[spring annotation(스프링 어노텐이션) 정리]]></title>
            <link>https://velog.io/@path_creator/spring-annotation%EC%8A%A4%ED%94%84%EB%A7%81-%EC%96%B4%EB%85%B8%ED%85%90%EC%9D%B4%EC%85%98-%EC%A0%95%EB%A6%AC</link>
            <guid>https://velog.io/@path_creator/spring-annotation%EC%8A%A4%ED%94%84%EB%A7%81-%EC%96%B4%EB%85%B8%ED%85%90%EC%9D%B4%EC%85%98-%EC%A0%95%EB%A6%AC</guid>
            <pubDate>Sun, 19 May 2024 08:44:43 GMT</pubDate>
            <description><![CDATA[<p>이미지 출처 : <a href="https://codevang.tistory.com/258">https://codevang.tistory.com/258</a></p>
<h3 id="1-spring-boot-애플리케이션-관련">1. Spring Boot 애플리케이션 관련</h3>
<p><strong>사용되는 라이브러리</strong></p>
<pre><code class="language-java">import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;</code></pre>
<p><strong>@SpringBootApplication: *<em>스프링 부트 애플리케이션의 메인 클래스를 지정. 이 애노테이션은 @Configuration, @EnableAutoConfiguration, @ComponentScan을 포함.
*</em>@Configuration:</strong> 자바 기반의 설정 클래스를 지정.
<strong>@ComponentScan:</strong> 지정된 패키지에서 스프링 컴포넌트를 스캔.
*<em>@EnableAutoConfiguration: *</em>스프링 부트의 자동 설정 기능을 활성화.</p>
<h3 id="2-웹-계층-관련">2. 웹 계층 관련</h3>
<p><strong>사용되는 라이브러리</strong></p>
<pre><code class="language-java">import org.springframework.web.bind.annotation.RestController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.CrossOrigin;</code></pre>
<p><strong>@RestController:</strong> JSON 또는 XML 형태로 직접 응답을 반환하는 컨트롤러 클래스를 지정.
*<em>@Controller: *</em>웹 요청을 처리하는 컨트롤러 클래스를 지정. 뷰(View)를 반환.</p>
<p><strong>RestController 예시 참조 :</strong> <a href="https://kingchan223.tistory.com/57">https://kingchan223.tistory.com/57</a></p>
<p><strong>Controller 예시 참조 :</strong> <a href="https://develop-log-sj.tistory.com/entry/Spring-Controller-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98%EA%B3%BC-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-%ED%83%80%EC%9E%85">https://develop-log-sj.tistory.com/entry/Spring-Controller-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98%EA%B3%BC-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-%ED%83%80%EC%9E%85</a></p>
<p><strong>@RequestMapping: **클래스나 메서드에 요청 URL을 매핑.
데이터 처리 애노테이션 (</strong>주 사용 :** <strong>controller class or method</strong>):</p>
<blockquote>
<p><strong>예: 데이터형태와 적용코드 예시</strong>
/users</p>
</blockquote>
<pre><code>@RequestMapping(value = &quot;/users&quot;, method = RequestMethod.GET)
public List&lt;User&gt; getUsers()</code></pre><p><strong>@GetMapping:</strong> HTTP GET 요청을 매핑.
<strong>@PostMapping:</strong> HTTP POST 요청을 매핑.
<strong>@PutMapping: *<em>HTTP PUT 요청을 매핑.
*</em>@DeleteMapping:</strong> HTTP DELETE 요청을 매핑.
<strong>@PatchMapping:</strong> HTTP PATCH 요청을 매핑.
데이터 처리 애노테이션 (<strong>주 사용 :</strong> <strong>controller class or method</strong>):</p>
<blockquote>
<p><strong>예: 데이터형태와 적용코드 예시</strong>
/products/{id}</p>
</blockquote>
<pre><code>@PatchMapping(&quot;/products/{id}&quot;)
public Product partialUpdateProduct(@PathVariable Long id, @RequestBody Map&lt;String, Object&gt; updates)</code></pre><p><strong>일반 mapping 예시 참조 :</strong> <a href="https://cordcat.tistory.com/88">https://cordcat.tistory.com/88</a></p>
<p><strong>requestmapping 및 crud 예시 참조 :</strong> <a href="https://velog.io/@zhyun/Spring-Boot-POST-Method-%EC%9A%94%EC%B2%AD-%EB%B0%9B%EA%B8%B0">https://velog.io/@zhyun/Spring-Boot-POST-Method-%EC%9A%94%EC%B2%AD-%EB%B0%9B%EA%B8%B0</a></p>
<p><strong>@RequestParam:</strong> 요청 매개변수를 메서드 인수에 매핑.
데이터 바인딩 애노테이션 (<strong>주 사용 :</strong> <strong>controller parameter</strong>):</p>
<blockquote>
<p><strong>예: 데이터형태와 적용코드 예시</strong> 
<em>?key=value, 
/products?category=electronics&amp;sort=price</em></p>
</blockquote>
<pre><code>@GetMapping(&quot;/products&quot;)
public List&lt;Product&gt; getProducts(
    @RequestParam String category,
    @RequestParam String sort
) </code></pre><p><strong>@PathVariable:</strong> URL 경로의 일부를 메서드 인수에 매핑.
데이터 바인딩 애노테이션 (<strong>주 사용 :</strong> <strong>controller parameter</strong>):</p>
<blockquote>
<p><strong>예: 데이터형태와 적용코드 예시</strong> 
<em>/users/{id}, 
/products/{productId}/reviews/{reviewId}</em></p>
</blockquote>
<pre><code> @GetMapping(&quot;/users/{userId}/orders/{orderId}&quot;)
public Order getOrderByUserAndOrderId
(@PathVariable Long userId, 
    @PathVariable Long orderId)</code></pre><p><strong>@RequestBody: **요청 본문을 메서드 인수에 매핑.
데이터 바인딩 애노테이션 (</strong>주 사용 :** <strong>controller parameter</strong>):
예: <em>{ &quot;key&quot;: &quot;value&quot; }</em></p>
<p><strong>@ResponseBody: **메서드의 반환 값을 HTTP 응답 본문에 매핑
데이터 반환 처리 (</strong>주 사용** : <strong>controller parameter</strong>)
/hello</p>
<blockquote>
<p><strong>예: 데이터 형태와 적용 코드 예시</strong></p>
</blockquote>
<pre><code>@ResponseBody
@GetMapping(&quot;/hello&quot;)
public String sayHello() </code></pre><p><strong>@RequestHeader:</strong> 요청 헤더 값을 메서드 인수에 매핑.</p>
<p><strong>@MatrixVariable:</strong> 필터링/조건 등을 URL 매개변수로 처리
복합적 필터링 조건 처리 (<strong>주 사용</strong> : <strong>controller parameter</strong>)</p>
<blockquote>
<p><strong>예: 데이터 형태와 적용 코드 예시</strong>
/products/category;key1=value1;key2=value2</p>
</blockquote>
<pre><code>@GetMapping(&quot;/products/{category}&quot;)
public List&lt;Product&gt; getProducts(
    @PathVariable String category,
    @MatrixVariable Map&lt;String, String&gt; filters
)</code></pre><p><strong>@CrossOrigin:</strong> 다른 출처(도메인)에서의 요청을 허용.</p>
<h3 id="3-데이터-계층-관련">3. 데이터 계층 관련</h3>
<p><strong>사용되는 라이브러리</strong>
만약 <strong>javax.persistence.</strong>이 인식 못하면 <strong>jakarta.persistence.</strong>으로 변환</p>
<pre><code class="language-java">import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Id;
import javax.persistence.GeneratedValue;
import javax.persistence.Column;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.ManyToMany;
import javax.persistence.OneToOne;
import org.springframework.stereotype.Repository;</code></pre>
<p><strong>@Entity:</strong> JPA 엔터티 클래스를 지정.
<strong>@Table:</strong> 엔터티 클래스와 매핑될 테이블을 지정.
<strong>@Id:</strong> 엔터티의 기본 키 필드를 지정.
<strong>@GeneratedValue:</strong> 기본 키 생성 전략을 지정.
<strong>@Column:</strong> 테이블의 컬럼을 매핑.</p>
<p>*<em>GeneratedValue 참조 예제: *</em> <a href="https://mantaray.tistory.com/101">https://mantaray.tistory.com/101</a></p>
<p>*<em>GeneratedValue 전략 예제: *</em> <a href="https://velog.io/@junsu1222/%EA%B8%B0%EB%B3%B8-%ED%82%A4-%EB%A7%A4%ED%95%91%EA%B3%BC-GeneratedValue%EC%A0%84%EB%9E%B5%EB%93%A4">https://velog.io/@junsu1222/%EA%B8%B0%EB%B3%B8-%ED%82%A4-%EB%A7%A4%ED%95%91%EA%B3%BC-GeneratedValue%EC%A0%84%EB%9E%B5%EB%93%A4</a></p>
<p><strong>@ManyToOne:</strong> 다대일 관계를 지정.
<strong>@OneToMany:</strong> 일대다 관계를 지정.
<strong>@ManyToMany:</strong> 다대다 관계를 지정.
<strong>@OneToOne:</strong> 일대일 관계를 지정.
<strong>@Repository:</strong> 데이터 접근 객체(DAO) 클래스를 지정.</p>
<p><strong>@ManyToOne+@OneToMany 예시 참조 :</strong> <a href="https://stir.tistory.com/158">https://stir.tistory.com/158</a></p>
<p><strong>@ManyToOne+@OneToMany 기능들 설명 :</strong> <a href="https://velog.io/@goniieee/JPA-OneToMany-ManyToOne%EC%9C%BC%EB%A1%9C-%EC%97%B0%EA%B4%80%EA%B4%80%EA%B3%84-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0">https://velog.io/@goniieee/JPA-OneToMany-ManyToOne%EC%9C%BC%EB%A1%9C-%EC%97%B0%EA%B4%80%EA%B4%80%EA%B3%84-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0</a></p>
<h3 id="4-서비스-계층-관련">4. 서비스 계층 관련</h3>
<p><strong>사용되는 라이브러리</strong></p>
<pre><code class="language-java">import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;</code></pre>
<p><strong>@Service:</strong> 서비스 클래스(비즈니스 로직을 처리하는 클래스)를 지정.
<strong>@Transactional:</strong> 메서드나 클래스에 트랜잭션 설정을 적용.</p>
<h3 id="5-구성-관련">5. 구성 관련</h3>
<p><strong>사용되는 라이브러리</strong></p>
<pre><code class="language-java">import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.Conditional;</code></pre>
<p><strong>@Value:</strong> 속성 값을 주입하는 데 사용. Spring의 환경 설정이나 프로퍼티 파일에서 값을 읽어와 필드에 주입.
 &nbsp;&nbsp; @Value(&quot;${property.name}&quot;) private String propertyName; 
<strong>@PropertySource:</strong> 외부 프로퍼티 파일을 로드하여 스프링 환경에 추가. 이는 @Configuration 클래스와 함께 사용되며, 다양한 형식의 프로퍼티 파일을 로드 가능.
 &nbsp;&nbsp; @PropertySource(&quot;classpath:application.properties&quot;)
<strong>@Bean:</strong> 스프링 컨텍스트에 빈을 정의하는 데 사용. @Configuration 클래스의 메서드에 붙여서 사용하며, 메서드의 반환 값을 스프링 빈으로 등록. 이는 스프링이 애플리케이션을 구성하는 방법 중 하나로, 외부 라이브러리의 클래스나 복잡한 초기화가 필요한 클래스의 빈을 정의하는 데 유용.</p>
<p>*<em>configuration, bean, component 용도 차이 : *</em><a href="https://mangkyu.tistory.com/75">https://mangkyu.tistory.com/75</a></p>
<p><strong>@Scope:</strong> 빈의 범위를 지정합니다. 스프링은 다양한 범위를 지원하며, 기본은 싱글톤(singleton). 다른 범위로는 프로토타입(prototype), 요청(request), 세션(session), 애플리케이션(application) 등이 존재.
 &nbsp;&nbsp; @Scope(&quot;prototype&quot;)
<strong>@Conditional:</strong> 특정 조건에 따라 빈을 정의. @Conditional 어노테이션은 특정 조건 클래스가 참을 반환하는 경우에만 빈을 생성. 이를 통해 애플리케이션의 설정이나 환경에 따라 빈의 생성 여부를 제어.
 &nbsp;&nbsp; @Conditional(MyCondition.class)</p>
<h3 id="6-의존성-주입-관련">6. 의존성 주입 관련</h3>
<p><strong>사용되는 라이브러리</strong>
만약 <strong>javax.</strong>이 인식 못하면 <strong>jakarta.</strong>으로 변환</p>
<pre><code class="language-java">import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import javax.annotation.Resource;
import javax.inject.Inject;</code></pre>
<p><strong>@Autowired:</strong> 의존성을 자동으로 주입. 스프링 컨텍스트에서 적절한 빈을 찾아 필드, 생성자, 또는 메서드에 주입합니다. 이를 통해 의존성 주입을 간편하게 처리할 수 있으며, 필요한 빈이 스프링 컨텍스트에 존재하지 않으면 애플리케이션이 실패.
 &nbsp;&nbsp; @Autowired private MyRepository myRepository;</p>
<p>*<em>autowire 와 di 예제 : *</em> <a href="https://mooonstar.tistory.com/entry/SpringDI%EC%99%80-Autowired-%ED%99%95%EC%8B%A4%ED%9E%88-%EC%95%8C%EA%B3%A0-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0">https://mooonstar.tistory.com/entry/SpringDI%EC%99%80-Autowired-%ED%99%95%EC%8B%A4%ED%9E%88-%EC%95%8C%EA%B3%A0-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</a></p>
<p><strong>@Qualifier:</strong> 주입할 빈을 특정. @Autowired와 함께 사용되어 여러 빈 중 하나를 명확하게 지정 가능. 이는 동일한 타입의 여러 빈이 존재할 때 어떤 빈을 주입할지 지정하는 데 유용.
 &nbsp;&nbsp; @Autowired @Qualifier(&quot;specificBean&quot;) private MyService myService;
<strong>@Resource:</strong> JSR-250의 리소스를 주입합니다. @Autowired와 비슷하지만, 이름으로 빈을 주입하는 데 더 중점을 둡니다. 이 어노테이션은 스프링 빈뿐만 아니라 JNDI 리소스에도 사용.
 &nbsp;&nbsp; @Resource(name=&quot;myBean&quot;) private MyService myService;
<strong>@Inject:</strong> JSR-330의 의존성을 주입합니다. @Autowired와 유사하게 동작하지만, 자바 표준 어노테이션입니다. 이는 자바 표준 의존성 주입 어노테이션으로, 스프링뿐만 아니라 다른 DI 프레임워크에서도 사용.
 &nbsp;&nbsp; @Inject private MyRepository myRepository;</p>
<h3 id="7-유효성-검사-관련">7. 유효성 검사 관련</h3>
<p><strong>사용되는 라이브러리</strong>
만약 <strong>javax.</strong>이 인식 못하면 <strong>jakarta.</strong>으로 변환</p>
<pre><code class="language-java">import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Min;
import javax.validation.constraints.Max;
import javax.validation.constraints.Size;</code></pre>
<p><strong>@Valid:</strong> 객체의 유효성을 검사하는 데 사용됩니다. 주로 메서드 파라미터 또는 멤버 변수에 붙여서 사용하며, 유효성 검사 애노테이션과 함께 사용됩니다. 스프링 MVC에서 컨트롤러 메서드의 파라미터에 붙여서 요청 데이터를 검증하는 데 자주 사용.
 &nbsp;&nbsp; public void process(@Valid MyEntity entity) { ... }
<strong>@NotNull:</strong> 필드가 null이 아님을 명시. 이는 데이터베이스나 비즈니스 로직에서 필드가 반드시 존재해야 할 때 사용.
 &nbsp;&nbsp; @NotNull private String name;
<strong>@NotEmpty:</strong> 문자열이나 컬렉션이 비어 있지 않음을 명시. null이 아니고, 길이가 0이 아니어야 합니다. 이는 사용자가 입력한 데이터가 비어 있지 않도록 하는 데 유용.
 &nbsp;&nbsp; @NotEmpty private List<String> items;
<strong>@NotBlank:</strong> 문자열이 공백이 아님을 명시. null, 빈 문자열, 공백만 있는 문자열은 유효하지 않음. 이는 사용자가 입력한 문자열이 실제로 유의미한 값을 가지도록 보장.
 &nbsp;&nbsp; @NotBlank private String name;
<strong>@Min:</strong> 필드의 최소값을 지정.
<strong>@Max:</strong> 필드의 최대값을 지정.
<strong>@Size:</strong> 문자열이나 컬렉션의 크기를 지정.</p>
<h3 id="8-기타">8. 기타</h3>
<p><strong>사용되는 라이브러리</strong></p>
<pre><code class="language-java">import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.annotation.Async;</code></pre>
<p><strong>@Component:</strong> 스프링의 일반적인 컴포넌트를 지정. 서비스, 리포지토리, 컨트롤러와 같이 사용됩니다. 스프링이 자동으로 감지하고 빈으로 등록.
<strong>@ControllerAdvice:</strong> 전역 예외 처리를 제공.
<strong>@ExceptionHandler:</strong> 특정 예외를 처리하는 메서드를 지정. @ControllerAdvice나 특정 컨트롤러 내에서 사용.
 &nbsp;&nbsp; @ExceptionHandler(NullPointerException.class) public String handleNullPointerException() { return &quot;error&quot;; }
<strong>@RestControllerAdvice:</strong> 전역 예외 처리를 제공하며 JSON/XML 형태로 응답을 반환.
<strong>@Scheduled:</strong> 메서드를 일정 시간 간격으로 실행. 주기적인 작업을 실행하는 데 사용.
<strong>@Async:</strong> 비동기적으로 메서드를 실행하도록 합니다. 비동기 작업을 간편하게 수행.</p>
<h3 id="9aop">9.AOP</h3>
<p><strong>사용되는 라이브러리</strong></p>
<pre><code class="language-java">import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;</code></pre>
<p><strong>@Aspect:</strong> @Aspect는 AspectJ 라이브러리에서 제공하는 어노테이션으로, 해당 클래스가 애스펙트(관점)임을 나타냄.
애스펙트는 AOP의 핵심 요소로, 특정 관심사를 모듈화하여 주된 비즈니스 로직과 분리. 예를 들어, 로깅, 보안, 트랜잭션 관리 등을 애스펙트로 구현 가능.
<strong>@Pointcut:</strong> @Pointcut은 AspectJ에서 제공하는 어노테이션으로, 특정 조인 포인트(Join Point)를 정의.
포인트컷은 어디에서 애스펙트가 적용될지를 결정. 메서드, 클래스, 패키지 등 특정 지점에 적용할 수 있습니다. 포인트컷 표현식을 사용하여 조인 포인트를 선택함.
<strong>@Before:</strong> @Before는 AspectJ에서 제공하는 어노테이션으로, 지정된 포인트컷의 조인 포인트 전에 실행될 어드바이스(Advice)를 정의.
주로 메서드 호출 전에 실행할 코드를 정의하는 데 사용. 예를 들어, 메서드 호출 전에 로깅하거나 인증을 확인하는 등의 작업을 수행 가능.
<strong>@After:</strong> @After는 AspectJ에서 제공하는 어노테이션으로, 지정된 포인트컷의 조인 포인트 후에 실행될 어드바이스를 정의.
메서드 실행 후에 실행할 코드를 정의하는 데 사용. 예를 들어, 리소스 정리나 메서드 실행 후 로깅을 수행 가능.
<strong>@AfterReturning:</strong> @AfterReturning은 AspectJ에서 제공하는 어노테이션으로, 포인트컷의 조인 포인트가 정상적으로 종료된 후 실행될 어드바이스를 정의.
메서드가 성공적으로 반환된 후 실행할 코드를 정의합니다. 반환된 값을 조작하거나 로깅할 때 유용.
<strong>@AfterThrowing:</strong> @AfterThrowing은 AspectJ에서 제공하는 어노테이션으로, 포인트컷의 조인 포인트가 예외를 던진 후 실행될 어드바이스를 정의.
메서드가 예외를 던진 후 실행할 코드를 정의. 예외 처리나 에러 로깅을 수행할 때 유용.
<strong>@Around:</strong> @Around는 AspectJ에서 제공하는 어노테이션으로, 포인트컷의 조인 포인트 전후에 실행될 어드바이스를 정의.
메서드 실행 전후에 실행할 코드를 정의. 주로 메서드 실행 시간을 측정하거나 메서드 실행 전후에 추가적인 작업을 수행할 때 사용.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[spring을 위한 gitignore 설정]]></title>
            <link>https://velog.io/@path_creator/spring%EC%9D%84-%EC%9C%84%ED%95%9C-gitignore-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@path_creator/spring%EC%9D%84-%EC%9C%84%ED%95%9C-gitignore-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Wed, 15 May 2024 17:58:05 GMT</pubDate>
            <description><![CDATA[<p><strong>깃허브</strong>에 코드를 올릴때 <strong>gitignore</strong>로 설정에 필요없는 버전이나 설정들은 무시하고 코드를 push(업로드) 하도록 한다. 이러한 방법은 <strong>협업 개발 환경</strong>의 <strong>버젼 충돌</strong>을 최대한 줄인다.
<img src="https://velog.velcdn.com/images/path_creator/post/350f9f4b-9b85-41df-8527-35c0c64f12a0/image.png" alt=""></p>
<p>gitignore 링크에서 gitignore를 다운받아서 사용한다.
<a href="https://www.toptal.com/developers/gitignore">https://www.toptal.com/developers/gitignore</a></p>
<p>스프링에서는 아래와 같이 gitignore를 설정한다.
<img src="https://velog.velcdn.com/images/path_creator/post/0680350c-c8f2-44cc-8f78-4b525ad5a4c5/image.png" alt=""></p>
<p><strong>QueryDsl 사용시: [필수]</strong> Querydsl의 QClass를 담는 generated는 자동생성되는 파일들의 디렉토리이니 무조건 generated을 .gitignore에 추가하셔야 합니다.</p>
<p>이처럼 파일을 뽑으면 아래와 같이 된다.</p>
<pre><code class="language-shell"># Created by https://www.toptal.com/developers/gitignore/api/windows,macos,git,java,maven,eclipse,intellij,gradle,netbeans,visualstudiocode
# Edit at https://www.toptal.com/developers/gitignore?templates=windows,macos,git,java,maven,eclipse,intellij,gradle,netbeans,visualstudiocode

### Eclipse ###
.metadata
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
.recommenders

# External tool builders
.externalToolBuilders/

# Locally stored &quot;Eclipse launch configurations&quot;
*.launch

# PyDev specific (Python IDE for Eclipse)
*.pydevproject

# CDT-specific (C/C++ Development Tooling)
.cproject

# CDT- autotools
.autotools

# Java annotation processor (APT)
.factorypath

# PDT-specific (PHP Development Tools)
.buildpath

# sbteclipse plugin
.target

# Tern plugin
.tern-project

# TeXlipse plugin
.texlipse

# STS (Spring Tool Suite)
.springBeans

# Code Recommenders
.recommenders/

# Annotation Processing
.apt_generated/
.apt_generated_test/

# Scala IDE specific (Scala &amp; Java development for Eclipse)
.cache-main
.scala_dependencies
.worksheet

# Uncomment this line if you wish to ignore the project description file.
# Typically, this file would be tracked if it contains build/dependency configurations:
#.project

### Eclipse Patch ###
# Spring Boot Tooling
.sts4-cache/

### Git ###
# Created by git for backups. To disable backups in Git:
# $ git config --global mergetool.keepBackup false
*.orig

# Created by git when using merge tools for conflicts
*.BACKUP.*
*.BASE.*
*.LOCAL.*
*.REMOTE.*
*_BACKUP_*.txt
*_BASE_*.txt
*_LOCAL_*.txt
*_REMOTE_*.txt

/src/main/generated

### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839

# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf

# AWS User-specific
.idea/**/aws.xml

# Generated files
.idea/**/contentModel.xml

# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml

# Gradle
.idea/**/gradle.xml
.idea/**/libraries

# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn.  Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr

# CMake
cmake-build-*/

# Mongo Explorer plugin
.idea/**/mongoSettings.xml

# File-based project format
*.iws

# IntelliJ
out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Cursive Clojure plugin
.idea/replstate.xml

# SonarLint plugin
.idea/sonarlint/

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties

# Editor-based Rest Client
.idea/httpRequests

# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser

### Intellij Patch ###
# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721

# *.iml
# modules.xml
# .idea/misc.xml
# *.ipr

# Sonarlint plugin
# https://plugins.jetbrains.com/plugin/7973-sonarlint
.idea/**/sonarlint/

# SonarQube Plugin
# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
.idea/**/sonarIssues.xml

# Markdown Navigator plugin
# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
.idea/**/markdown-navigator.xml
.idea/**/markdown-navigator-enh.xml
.idea/**/markdown-navigator/

# Cache file creation bug
# See https://youtrack.jetbrains.com/issue/JBR-2257
.idea/$CACHE_FILE$

# CodeStream plugin
# https://plugins.jetbrains.com/plugin/12206-codestream
.idea/codestream.xml

# Azure Toolkit for IntelliJ plugin
# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij
.idea/**/azureSettings.xml

### Java ###
# Compiled class file
*.class

# Log file
*.log

# BlueJ files
*.ctxt

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
replay_pid*

### macOS ###
# General
.DS_Store
.AppleDouble
.LSOverride

# Icon must end with two \r
Icon


# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

### macOS Patch ###
# iCloud generated files
*.icloud

### Maven ###
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
# https://github.com/takari/maven-wrapper#usage-without-binary-jar
.mvn/wrapper/maven-wrapper.jar

# Eclipse m2e generated files
# Eclipse Core
.project
# JDT-specific (Eclipse Java Development Tools)
.classpath

### NetBeans ###
**/nbproject/private/
**/nbproject/Makefile-*.mk
**/nbproject/Package-*.bash
build/
nbbuild/
dist/
nbdist/
.nb-gradle/

### VisualStudioCode ###
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets

# Local History for Visual Studio Code
.history/

# Built Visual Studio Code Extensions
*.vsix

### VisualStudioCode Patch ###
# Ignore all local history of files
.history
.ionide

### Windows ###
# Windows thumbnail cache files
Thumbs.db
Thumbs.db:encryptable
ehthumbs.db
ehthumbs_vista.db

# Dump file
*.stackdump

# Folder config file
[Dd]esktop.ini

# Recycle Bin used on file shares
$RECYCLE.BIN/

# Windows Installer files
*.cab
*.msi
*.msix
*.msm
*.msp

# Windows shortcuts
*.lnk

### Gradle ###
.gradle
**/build/
!src/**/build/

# Ignore Gradle GUI config
gradle-app.setting

# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar

# Avoid ignore Gradle wrappper properties
!gradle-wrapper.properties

# Cache of project
.gradletasknamecache

# Eclipse Gradle plugin generated files
# Eclipse Core
# JDT-specific (Eclipse Java Development Tools)

### Gradle Patch ###
# Java heap dump
*.hprof

# End of https://www.toptal.com/developers/gitignore/api/windows,macos,git,java,maven,eclipse,intellij,gradle,netbeans,visualstudiocode
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[깃허브 협업을 위한 push, pull기초]]></title>
            <link>https://velog.io/@path_creator/%EA%B9%83%ED%97%88%EB%B8%8C-%ED%98%91%EC%97%85%EC%9D%84-%EC%9C%84%ED%95%9C-%EA%B8%B0%EC%B4%88</link>
            <guid>https://velog.io/@path_creator/%EA%B9%83%ED%97%88%EB%B8%8C-%ED%98%91%EC%97%85%EC%9D%84-%EC%9C%84%ED%95%9C-%EA%B8%B0%EC%B4%88</guid>
            <pubDate>Wed, 15 May 2024 14:26:28 GMT</pubDate>
            <description><![CDATA[<p>Git 다운로드 필요
멤버는 총 2명이라는 가정하에 설명. (책임, 1번 개발 멤버)</p>
<h2 id="알아두어야할-기본-개념">알아두어야할 기본 개념</h2>
<p><strong>주제 :</strong> 깃헙 푸시 풀 브랜치의 활용
<strong>푸시 (push) :</strong> 내가 제작한 코드를 업로드
<strong>풀 (pull) :</strong> 깃헙에 코드를 불러서 내코드에 적용
<strong>커밋 (commit) :</strong> 해당작업에 푸시나 풀이전에 메세지를 남기기 위한 작업
<strong>브랜치 :</strong> 깃헙에 푸시 밑 풀기록을 남기기 위한 작업 이름 (main 브랜치와 동일한 작업 이름은 사용 하지 않는다.)
<strong>git init :</strong> 현재 폴더가 Git의 버전 관리아래에 들어감
<strong>git add --all  :</strong> 깃의 버전이나 깃의 작업 변경 사항 적용
<strong>git add . :</strong> 현재 터미널에서 선택한 디렉토리(폴더)에서의 버전이나 깃의 작업 변경 사항 적용</p>
<p>git init 입력 -&gt; git add --all또는 git add . -&gt; 커밋 -&gt; 푸시or풀</p>
<h2 id="최초의-깃허브-작업-세팅">최초의 깃허브 작업 세팅</h2>
<h3 id="개발-_책임_의-작업">개발 _책임_의 작업</h3>
<p><strong>1. 깃허브 사이트에서 repository 생성</strong></p>
<p><img src="https://velog.velcdn.com/images/path_creator/post/b59fbd4c-4bae-4126-9c11-041bd665ec38/image.png" alt="">
<em><strong>참조 사진 -1</strong></em></p>
<p><strong>2. github 관리 전용 디렉토리(폴더) 생성 **
-선택이긴 하지만 강력 추천
*<em>3. github 관리 전용 디렉토리(폴더) 안에 개발 환경 생성 *</em>
**4. 터미널로 개발 환경 디렉토리로 들어가 아래와같이 git init입력</strong>  </p>
<blockquote>
<pre><code class="language-shell">PS C:\users\user\Documents\github\untitled&gt; git init /좌측처럼 입력
Initialized empty Git repository in C:/Users/user/Documents/github/untitled/.git/ --메세지</code></pre>
</blockquote>
<pre><code>
**git init :** 현재 폴더가 Git의 버전 관리아래에 들어감
**5. 터미널로 개발 환경 디렉토리로 들어가 아래와같이 &#39; git remote add origin 깃레포지토리 주소 &#39; 입력 **
_**참조 사진 -1**_에 깃 레포지토리 주소가 &lt;span style=&quot;color: green&quot;&gt;**초록색 상자**&lt;/span&gt;로 표시
&gt; ```shell
git remote add origin https://github.com/be-01-team/practice-spring-api.git /좌측처럼 입력
git add --all  /좌측처럼 입력 </code></pre><blockquote>
<pre><code class="language-shell">--아래는 메세지
warning: in the working copy of &#39;.gitignore&#39;, LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of &#39;.idea/misc.xml&#39;, LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of &#39;gradlew&#39;, LF will be replaced by CRLF the next time Git touches it
warning: in the working copy of &#39;src/main/java/com/example/StreamExample1.java&#39;, LF will be replaced by CRLF the next time Git touches it -- 메세지</code></pre>
</blockquote>
<pre><code>
**6. 깃허브 브라우저나 어플에서의 요청 허가**
(깃헙에서 요청을 안하는 경우도 있으나 문제는 안됨)
![](https://velog.velcdn.com/images/path_creator/post/1bcf9001-dda2-4bdf-a616-e77a47cdc800/image.png)

**7. 깃허브 커밋 (commit) 입력으로 첫 코드 푸시전 환경 세팅 **
&gt; ```shell
git commit -m &quot;first initalized codes&quot;  /좌측처럼 입력</code></pre><blockquote>
<pre><code class="language-shell">--아래는 메세지
[master (root-commit) 3c60ce1] first initalized codes
 24 files changed, 910 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 .idea/.gitignore
 create mode 100644 .idea/encodings.xml
 create mode 100644 .idea/gradle.xml
 create mode 100644 .idea/misc.xml
 create mode 100644 build.gradle
 create mode 100644 gradle/wrapper/gradle-wrapper.jar
 create mode 100644 gradle/wrapper/gradle-wrapper.properties
 create mode 100644 gradlew
 create mode 100644 gradlew.bat
 create mode 100644 settings.gradle
 create mode 100644 src/main/generated/com/example/repository/QAcademy.java
 create mode 100644 src/main/generated/com/example/repository/QTodo.java
 create mode 100644 src/main/java/com/example/Customer.java
 create mode 100644 src/main/java/com/example/StreamExample1.java
 create mode 100644 src/main/java/com/example/StringUtils.java
 create mode 100644 src/main/java/com/example/config/QueryDslConfig.java
 create mode 100644 src/main/java/com/example/repository/Academy.java
 create mode 100644 src/main/java/com/example/repository/AcademyRepository.java
 create mode 100644 src/main/java/com/example/repository/AcademyRepositorySupport.java
 create mode 100644 src/main/java/com/example/repository/Todo.java
 create mode 100644 src/main/java/com/example/repository/TodoRepository.java
 create mode 100644 src/main/java/com/example/repository/TodoRepositoryCustom.java
 create mode 100644 src/test/java/BasicTest.java --메세지
 &#39;&#39;&#39;</code></pre>
</blockquote>
<p>커밋 개념 : git commit -m(메세지의 줄임말) &quot;first initalized codes&quot;(해당 작업에 대한 명시를 해야 협업시 수월한 협업 가능)
더하여 커밋이 안되있다면 어떤한 pull이나 push가 작동 불가</p>
<p>*<em>8. 깃허브 브랜치 이름 확인 후, git push -u origin master 입력으로 첫 코드 push 성공 *</em></p>
<blockquote>
<pre><code class="language-shell">PS C:\users\user\Documents\github\untitled&gt; git branch /좌측처럼 입력</code></pre>
</blockquote>
<ul>
<li>master</li>
<li>-브랜치 확인 후
PS C:\users\user\Documents\github\untitled&gt; git push -u origin master /이렇게 입력<pre><code></code></pre></li>
</ul>
<blockquote>
<pre><code class="language-shell">--아래는 메세지
Enumerating objects: 42, done.
Counting objects: 100% (42/42), done.
Delta compression using up to 12 threads
Compressing objects: 100% (31/31), done.
Writing objects: 100% (42/42), 65.88 KiB | 13.18 MiB/s, done.
Total 42 (delta 1), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (1/1), done.
To https://github.com/be-01-team/practice-spring-api.git</code></pre>
</blockquote>
<ul>
<li>[new branch]      master -&gt; master
branch &#39;master&#39; set up to track &#39;origin/master&#39;.<pre><code></code></pre></li>
</ul>
<p>push 개념 : git push -u (upstream 약자로 해당 브랜치로 업로드시 그 브랜치가 다음에도 기본으로 브랜치로 변경) origin (해당 원격저장소의 기본 default) master(브랜치 이름)</p>
<p>아까 그 repository로 들어가면 아래사진과 같이 업로드 완료</p>
<p><img src="https://velog.velcdn.com/images/path_creator/post/30a3810f-4b1d-4c8d-84fa-f9a855bda5e4/image.png" alt=""></p>
<p>이렇게 브랜치 명칭에 맞게 생성 최초로 올린 브랜치의 이름이 기본 브랜치가 되며 그 브랜치가 프로젝트 총 책임 브랜치로 되어야한다. </p>
<h4 id="총-책임-브랜치가-사용되는-경우">총 책임 브랜치가 사용되는 경우</h4>
<ul>
<li>최초의 repository 생성시</li>
<li>PR(Pull request) merge를 할때 - 즉, 멤버의 개발 코드를 병합하여 업데이트할 경우</li>
</ul>
<p>이거 이외는 이 브랜치를 사용하지 않도록 한다. 왜냐면 분리된 브랜치로 코드의 변형을 막기 위함이다.</p>
<h3 id="요약">요약</h3>
<p>git init
git remote add origin <a href="https://github.com/">https://github.com/</a>....
git add --all 
git commit -m &quot;first initalized codes&quot;
git branch
git push -u origin master</p>
<h2 id="멤버의-최초의-깃허브-코드-받기">멤버의 최초의 깃허브 코드 받기</h2>
<h3 id="개발-_1번-멤버_의-작업">개발 _1번 멤버_의 작업</h3>
<p><strong>깃허브에 기본 코드 틀이 올라간 이후, 개발 멤버가 코드를 받는다</strong></p>
<p><strong>9. 터미널로 개발 환경 디렉토리로 들어가 아래와 같이 git init입력 **
**그 이후, git clone <a href="https://github.com/be-01-team/practice-spring-api">https://github.com/be-01-team/practice-spring-api</a> (리포 주소) 입력</strong></p>
<blockquote>
<pre><code class="language-shell">PS C:\kotlintutorial\practice-spring-api&gt; git init /좌측처럼 입력
PS C:\kotlintutorial\practice-spring-api&gt; git clone https://github.com/be-01-team/practice-spring-api /좌측처럼 입력</code></pre>
</blockquote>
<pre><code>&gt; ```shell
--아래는 메세지
Cloning into &#39;practice-spring-api&#39;...
remote: Enumerating objects: 42, done.
remote: Counting objects: 100% (42/42), done.
remote: Compressing objects: 100% (30/30), done.
remote: Total 42 (delta 1), reused 42 (delta 1), pack-reused 0
Receiving objects: 100% (42/42), 65.88 KiB | 5.07 MiB/s, done.
Resolving deltas: 100% (1/1), done. --메세지</code></pre><p><strong>git clone 개념 :</strong> git clone <a href="https://github.com/be-01-team/practice-spring-api">https://github.com/be-01-team/practice-spring-api</a> (레포지토리 주소) 이 개념은 해당 주소에 관련된 코드를 현재 디렉토리로 다운 받는다. </p>
<p><strong>10. 터미널로 second-kim 브랜치 생성</strong>
*<em>이유 : !!![필수] *</em>master브랜치에 영향을 주기 때문에 브랜치를 second-kim으로 변경 하고 작업 </p>
<blockquote>
<pre><code class="language-shell">PS C:\kotlintutorial\practice-spring-api\practice-spring-api&gt; git branch /좌측처럼 입력</code></pre>
</blockquote>
<ul>
<li>master
PS C:\kotlintutorial\practice-spring-api\practice-spring-api&gt; git branch second-kim /좌측처럼 입력
PS C:\kotlintutorial\practice-spring-api\practice-spring-api&gt; git branch</li>
<li>master
second-kim<pre><code></code></pre></li>
</ul>
<p><strong>11. 터미널로 master브랜치를 git checkout second-kim으로 second-kim으로 변경하여 작업</strong></p>
<blockquote>
<pre><code class="language-shell">PS C:\kotlintutorial\practice-spring-api\practice-spring-api&gt; git checkout second-kim /좌측처럼 입력
Switched to branch &#39;second-kim&#39;
PS C:\kotlintutorial\practice-spring-api\practice-spring-api&gt; git branch /좌측처럼 입력
  master</code></pre>
</blockquote>
<ul>
<li>second-kim<pre><code></code></pre></li>
</ul>
<p><strong>개발 환경에 깃환경을 정착하면 왼쪽 <span style="color: green">초록색 상자</span> 처럼 새로운 ui가 추가됨</strong>
이 상태에서 <code>master</code> 브랜치를 유지한 채로 개발하면 원격적으로 변경 가능성이 있음. 본 코드에 오류 가능성 유발, !!!따라서 <strong>[필수]</strong> 작업 브랜치로 변경 후 코드 작업 
<img src="https://velog.velcdn.com/images/path_creator/post/45717b8e-3b88-4e1d-9434-90c0a9dfafd6/image.png" alt=""></p>
<p><strong>개발 브랜치에 위와같이 추가 됨</strong>
<img src="https://velog.velcdn.com/images/path_creator/post/3ccabd73-7c04-41a0-83e8-8cd765b3bbba/image.png" alt=""></p>
<p><strong>12. git init 입력과 git add . 입력으로 git 환경 구성</strong></p>
<blockquote>
<pre><code class="language-shell">PS C:\kotlintutorial\practice-spring-api\practice-spring-api&gt; git init
Reinitialized existing Git repository in C:/kotlintutorial/practice-spring-api/practice-spring-api/.git/
PS C:\kotlintutorial\practice-spring-api\practice-spring-api&gt; git add --all
warning: in the working copy of &#39;.idea/misc.xml&#39;, LF will be replaced by CRLF the next time Git touches it</code></pre>
</blockquote>
<pre><code>
**13. 커밋 입력 후, git 환경 구성**
&gt; ```shell
PS C:\kotlintutorial\practice-spring-api\practice-spring-api&gt; git commit -m &quot;user list changed&quot;   /좌측처럼 입력
[second-kim e817c13] user list changed
 4 files changed, 23 insertions(+), 7 deletions(-)
 create mode 100644 .idea/jpa-buddy.xml
 create mode 100644 .idea/vcs.xml   --메세지</code></pre><p><strong>14. git push -u origin second-kim으로 코드 올림</strong>  </p>
<blockquote>
<pre><code class="language-shell">PS C:\kotlintutorial\practice-spring-api\practice-spring-api&gt; git push -u origin second-kim</code></pre>
</blockquote>
<pre><code>
&gt; ```shell
--아래는 메세지
Enumerating objects: 21, done.
Counting objects: 100% (21/21), done.
Delta compression using up to 4 threads
Compressing objects: 100% (10/10), done.
Writing objects: 100% (12/12), 1.26 KiB | 323.00 KiB/s, done.
Total 12 (delta 4), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (4/4), completed with 4 local objects.
remote:
remote: Create a pull request for &#39;second-kim&#39; on GitHub by visiting:
remote:      https://github.com/be-01-team/practice-spring-api/pull/new/second-kim
remote:
To https://github.com/be-01-team/practice-spring-api
 * [new branch]      second-kim -&gt; second-kim
branch &#39;second-kim&#39; set up to track &#39;origin/second-kim&#39;.</code></pre><p><strong>15. 위 작업 이후 second-kim 브랜치에 PR(pull request) 요청이 오면 깃허브에서 PR(pull request) create 그리고 코드 merge 및 업데이트</strong>
<img src="https://velog.velcdn.com/images/path_creator/post/85b99e7c-bcf0-4393-a608-5ec35756583e/image.png" alt=""></p>
<p><strong>해당 코드 작업</strong>
<img src="https://velog.velcdn.com/images/path_creator/post/03463d66-6680-4621-b015-f8bd90b5fb6c/image.png" alt=""></p>
<p><strong>해당 코드 작업 merge 이후, 코드 변경됨</strong></p>
<p><img src="https://velog.velcdn.com/images/path_creator/post/b80bd877-9570-4428-a011-ca28c40ab424/image.png" alt=""></p>
<p><strong>브랜치 기록</strong>
<img src="https://velog.velcdn.com/images/path_creator/post/b4c842cf-82ab-4ec9-acc5-e3cc7a471165/image.png" alt=""></p>
<h3 id="요약-1">요약</h3>
<p>git init
git clone <a href="https://github.com/">https://github.com/</a>...
git branch second-name
git checkout second-name 
git commit -m &quot;user list changed&quot;
git push -u origin second-kim
깃허브 사이트에서 PR 생성
책임 계정으로 PR merge로 코드 업데이트</p>
<h2 id="2번째-깃헙-업데이트-깃헙-코드-pull다운-받아서-개발-작업"><em>2번째 깃헙 업데이트</em> 깃헙 코드 pull(다운) 받아서 개발 작업</h2>
<p><strong>git init은 이미 했기에 git add --all 입력 -&gt; git commit -m &quot;pull finished work&quot; 입력</strong></p>
<blockquote>
<pre><code class="language-shell">PS C:\users\user\Documents\github\untitled&gt; git add --all /좌측처럼 입력
PS C:\users\user\Documents\github\untitled&gt; git commit -m &quot;pull finished work&quot;  /좌측처럼 입력</code></pre>
</blockquote>
<pre><code>&gt; ```shell
--아래는 메세지
[master 75aa2c8] pull finished work
 1 file changed, 6 insertions(+)
 create mode 100644 .idea/vcs.xml</code></pre><p>*<em>16. pull로 코드 다운받기 *</em></p>
<blockquote>
<pre><code class="language-shell">PS C:\users\user\Documents\github\untitled&gt; git pull origin master</code></pre>
</blockquote>
<pre><code>&gt; ```shell
--아래 메세지
From https://github.com/be-01-team/practice-spring-api
 * branch            master     -&gt; FETCH_HEAD
Auto-merging .idea/vcs.xml
CONFLICT (add/add): Merge conflict in .idea/vcs.xml
Automatic merge failed; fix conflicts and then commit the result.</code></pre><p><strong>17. manager라는 branch를 생성</strong></p>
<blockquote>
<pre><code class="language-shell">PS C:\users\user\Documents\github\untitled&gt; git branch manager
PS C:\users\user\Documents\github\untitled&gt; git branch
  manager</code></pre>
</blockquote>
<ul>
<li>master<pre><code></code></pre></li>
</ul>
<p><strong>18. git add --all 입력 후, manager로 브랜치 변경 그리고 코드 작업</strong></p>
<blockquote>
<pre><code class="language-shell">PS C:\users\user\Documents\github\untitled&gt; git add --all
PS C:\users\user\Documents\github\untitled&gt; git checkout manager
--아래 메세지
Switched to branch &#39;manager&#39;
A       .idea/jpa-buddy.xml
M       .idea/misc.xml
M       .idea/vcs.xml
M       src/main/java/com/example/StreamExample1.java</code></pre>
</blockquote>
<pre><code>
**19. 그리고 아까와 비슷한과정으로 코드 push를 하여 업데이트**


## 자주일어나는 오류 상황 해결 대처 법
#### 1. branch 명칭을 잘못 만들경우 branch 삭제
 브랜치 삭제 : git branch -d 브랜치이름
#### 2. git의 push 및 pull 기록들이 꼬여서 pull 받았을 경우
리포지토리에 가장 최신기록의 push와 pull로 덮어써서 업데이트하는 방법
git commit -m &#39;메세지&#39; 입력 이후 ,  git pull --rebase origin main 입력
#### 3. 잘못된 레포지토리 주소를 remote로 추가했을 경우 remote를 제거하는 법
git remote remove origin


## 하위 브랜치에서 오류 발생시 main으로 푸시하는 법
#### git checkout master

#### git rebase main

#### git fetch origin

#### git merge origin/main

#### git checkout main                           

#### git checkout -b new-feature-branch
Switched to a new branch &#39;new-feature-branch&#39;

#### git checkout master

#### git push origin main --force

#### git merge master
만약 이런 문구가 뜬다면 fatal: refusing to merge unrelated histories

### 추가적인 방법
#### git merge master --allow-unrelated-histories

#### git add .
#### git commit -m &quot;Resolved merge conflicts&quot;
#### git push origin main
</code></pre>]]></description>
        </item>
    </channel>
</rss>