2025년 8월 24일 일요일

[2025-08-24] vscode로 프로젝트 초기 설정 가이드

vscode로 프로젝트 초기 설정 가이드

안녕하세요. 클스 입니다.

우리가 서비스를 개발할때, front-end, back-end, batch 등 여러 하위 시스템들로 구성하는 경우가 많습니다.
이때 project 폴더를 만들고 하위에 폴더를 구성합니다.

full-stack을 혼자 다해야 하는 개발자는 vscode를 3개를 실행해서 편집하는 경우가 대부분입니다.

테미널도 3개를 띄워서 쓰는 경우가 있습니다. 이를 간단하게 해결하는 방법입니다.


1. 개요

솔루션, 서비스를 개발할때 project folder를 생성하고, 하위에 여러 기능들을 추가한다.
api, batch, user web, admin web, monitoring web...

이때 vscode의 workspace를 열면 자동으로 터미널까지 셋팅하게 할 수 있다.

2. 환경 설정(workspace)

  • project folder 생성
    $ mkdir myproject
    $ cd myproject
    $ mkdir .vscode
    $ code .
    
  • .vscode 폴더에서 tasks.json 작성
    {
      "version": "1.0.0",
      "tasks": [
        {
          "label": "admin-web",
          "type": "shell",
          "command": "${env:SHELL}",                   // macOS/Linux
          "windows": { "command": "powershell.exe" },  // Windows
          "options": {
            "cwd": "${workspaceFolder}/nginx-proxy" 
          },
          "presentation": {
            "panel": "dedicated",
            "group": "MyService",          //     group  
            "reveal": "always",
            "showReuseMessage": false
          },
          "problemMatcher": [],
          "runOptions": { "runOn": "folderOpen" }
        },
        {
          "label": "web-user",
          "type": "shell",
          "command": "${env:SHELL}",
          "windows": { "command": "powershell.exe" },
          "options": {
            "cwd": "${workspaceFolder}/web-user/trunk" 
          },
          "presentation": {
            "panel": "dedicated",
            "group": "MyService",
            "reveal": "always",
            "showReuseMessage": false
          },
          "problemMatcher": [],
          "runOptions": { "runOn": "folderOpen" }
        },
        {
          "label": "user-web-api",
          "type": "shell",
          "command": "${env:SHELL}",
          "windows": { "command": "powershell.exe" },
          "options": {
            "cwd": "${workspaceFolder}/web-user-api/trunk" 
          },
          "presentation": {
            "panel": "dedicated",
            "group": "MyService",
            "reveal": "always",
            "showReuseMessage": false
          },
          "problemMatcher": [],
          "runOptions": { "runOn": "folderOpen" }
        }
      ]
    }
    
  • 하위 프로젝트 폴더를 생성하고 workspace로 저장한다.
    {
      "folders": [
        {
          "path": ".",
          "name": "MyService",
          "editor.tabSize": 2,
        }
      ],
      "settings": {
        "files.exclude": {
          "**/.git": true,
          "**/.venv": true,
          "**/.nvmrc": true,
          "**/.quasar": true,
          "**/.files": true,
          "**/node_modules": true,
          "**/.DS_Store": true,
          "**/__pycache__": true
        }
      }
    }
    
  • tasks.json 파일 변경시 적용
1. 편집기 재실행 (간단하지만 확실한 방법)
   창 전체 Reload : 단축키: Ctrl+Shift+P → “Developer: Reload Window” 실행
   VS Code 창이 재시작되며 모든 설정(tasks.json 포함)이 새로 로드됩니다.

2. 워크스페이스 재열기
   메뉴: File > Close Folder 한 뒤 다시 Open Folder
   폴더 열기 시 runOn: "folderOpen" 옵션이 있는 태스크가 자동으로 실행됩니다.
  • svn ignore 설정하기
    # .svnignore 파일 생성
    .git
    .venv
    .nvmrc
    .quasar
    .files
    node_modules
    .DS_Store
    __pycache__
    
  • 커밋하기
    # 1) .svnignore 파일을 svn:ignore 속성으로 설정
    svn propset svn:ignore -F .svnignore .
    
    # 2) 변경 사항 커밋
    svn commit -m "Add svn:ignore based on VSCode files.exclude" 
  • code로 workspace 열기
    • workspace를 프로젝트 폴더에 생성했다면 myservice.code-workspace 파일이 있다.
    • $ code myservice.code-workspace 로 열면 필요한 Terminal이 한번에 열리게 된다.

3. 친절한 README.md 파일

  • project 폴더에 README.md : 개발자가 빠르게 개발, 빌드환경을 설정할 수 있도록 상세하게 적는다.
    하위 여러개의 프로젝트가 있으므로 전체를 기술한다. 그리고 각 프로젝트 폴더별 README.md를 생성하고 자세하게 필요한 내용을 가이드 한다. 전체 실행 방법, 테스트 계정등을 포함하면 좋다.
  • 신입 팀원이 들어와도 개발 환경 구축 부터 빠르게 할 수 있게 된다.
# web-user

## quasar 초기 설정
```bash
pnpm setup --global ===> .zshrc에 pnpm path 를 추가
pnpm add -g @quasar/cli
pnpm add -D vite@^6.0.0 => 프로젝트는 이 버전을 쓰네
```

## 모듈 설치
```bash
pnpm install --frozen-lockfile
```

## 로컬 구동
```bash
./start.sh
# or
nvm use
pnpm start:local-pwa
```

## 빌드
```bash
# 개발
pnpm build:dev-pwa

# 운영
pnpm build:prd-pwa
```

## Tips
```bash
# config 임시파일 삭제
pnpm quasar clean --qconf
```

라벨: , , , ,

2024년 9월 20일 금요일

[quaser.dev][2024-09-14] 윈도우즈(10, 64bit)에 개발환경 설정하기

[quaser.dev][2024-09-14] 윈도우즈(10, 64bit)에 개발환경 설정하기

자세한 내용 보기 »

라벨: , , , , ,

2023년 10월 21일 토요일

[2023-10-21] Python 3.12.0 버전 발표(Release Date: Oct. 2, 2023)

 

Python 3.12.0 버전 발표(Release Date: Oct. 2, 2023)

요약 : 파이썬 3.12, f 문자열 개선 등 개발 편의성 향상

Release Note

python 3.11 과 3.12 의 주요 기능 비교

* f 문자열은 복잡한 문자열 출력을 위한 문자열 형식화 기능의 하나다. 
  문자열 앞에 알파벳 f를 붙인 후 중괄호({}) 안에 공백 크기 지정 등 
  고급 형식지정 문자열을 입력해 바로 사용할 수 있다.

* 신규 버전에서는 기존에 지원하지 않던 여러 줄의 표현식, 주석, 백슬래시 및 유니코드 이스케이프 시퀀스를 
  처리할 수 있는 기능이 추가됐다.

* 표준라이브러리도 효율적인 활용을 목표로 수정이 이뤄졌다. 
  정리, 유용성 및 정확성에 중점을 둔 이번 수정작업에서 디스트유틸(distutils)패키지 등 더 이상 사용하지 않는 API는 제거되며 
  패스리브(Pathlib) 클래스가 서브클래싱을 지원하며, 명령줄 인터페이스가 모듈 SQLite3에 추가됐다.

* 더불어 성능 개선 작업을 통해 내장 함수의 하나인 이즈인스턴스(isinstance)는 약 2~20배 속도 향상 됐으며, 
  비동기 I/O(asyncio) 패키지도 일부 벤치마크에서 약 75% 빨라진 것으로 나타났다. .

* 파이썬 3.12버전은 인터프리터별 전역 해석기 잠금(GIL)을 적용해 여러 파이썬 스레드가 동시에 실행되는 것을 방지한다. 
  이를 통해 여러 CPU 코어를 보다 효율적으로 사용할 수 있는 하위 인터프리터 생성이 가능하다.

* 이 밖에도 리눅스 커널 성능 측정 도구(perf)에서 C파이썬(CPython)을 지원하는 등 
  성능 개선 및 편의성 지원을 위한 업데이트가 이뤄졌다.

* 출처 : https://zdnet.co.kr/view/?no=20231004094411
  • 공식 발표 내용
    * More flexible f-string parsing, allowing many things previously disallowed (PEP 701).
    * Support for the buffer protocol in Python code (PEP 688).
    * A new debugging/profiling API (PEP 669).
    * Support for isolated subinterpreters with separate Global Interpreter Locks (PEP 684).
    * Even more improved error messages. More exceptions potentially caused by typos now make suggestions to the user.
    * Support for the Linux perf profiler to report Python function names in traces.
    * Many large and small performance improvements (like PEP 709 and support for the BOLT binary optimizer), delivering an * estimated 5% overall performance improvement.
    
    

pyenv로 python 13.2.0 설치하기

$ pyenv install --list
---
  3.11.1
  3.11.2
  3.11.3
  3.11.4
  3.11.5
  3.11.6
  3.12.0
  3.12-dev
  3.13.0a1
  3.13-dev
---

$ pyenv install 3.12.0
python-build: use openssl@3 from homebrew
python-build: use readline from homebrew
Downloading Python-3.12.0.tar.xz...
-> https://www.python.org/ftp/python/3.12.0/Python-3.12.0.tar.xz
Installing Python-3.12.0...
python-build: use readline from homebrew
python-build: use ncurses from homebrew
python-build: use zlib from xcode sdk
Installed Python-3.12.0 to /Users/keulstar/.pyenv/versions/3.12.0
---

$ pyenv virtualenv 3.12.0 py312
$ pyenv local py312      : 로그인 사용자의 파이썬 버전 설정 
$ pyenv global py312     : 전체 사용자의 파이썬 버전 설정 
$ pyenv activate py312   : 현재 세션의 파이썬 버전 설정

vscode에서 python interpreter 설정하기 

shift + cmd + p 를 눌러서 아래를 실행하고,
입력란데 python select 하고 Python: 인터프리터를 선택한다.



mac에 설치된 python 목록이 아래와 같이 나온다. 원하는 버전을 선택하면 된다.

나는 py312로 가상환경을 만들었기 때문에 이것을 선택했다.


이상 클스 였습니다.

라벨: , , , , ,

2023년 4월 20일 목요일

[2023-04-19] vscode로 typescript 개발 환경(Menu 테이블을 Tree형태로 구성)

안녕하세요. 클스 입니다.

typescript 가 이젠 많이 중요해 졌습니다. vuejs, react 등에서 빠질 수 가 없네요~


개요

- DB의 메뉴 테이블에서 데이터를 가져와서 웹에 tree 구조로 출력해보려고 하니 웹을 올리고 소스 수정하면 빌드하고 확인하는 시간들이 많이 들어요~

- 그래서 typescript만 개발하면 좋을 듯하여 셋팅 해봤습니다.


메뉴 테이블 쿼리

WITH RECURSIVE tree_view(menu_id, menu_nm, up_menu_id, depth, path) AS (
    SELECT 
        menu_id, 
        menu_nm, 
        up_menu_id, 
        1 as depth, 
        CAST(menu_id AS VARCHAR(100)) as path
    FROM tb_menu
    WHERE up_menu_id is NULL -- 최상위 노드를 선택합니다
UNION ALL    
    SELECT 
        child.menu_id, 
        child.menu_nm, 
        child.up_menu_id, 
        parent.depth + 1 as depth, 
        CONCAT(parent.path, '_', child.menu_id) as path
    FROM tb_menu child
    JOIN tree_view parent 
      ON child.up_menu_id = parent.menu_id
)
SELECT 
    menu_id, menu_nm, up_menu_id, depth, path
    , CONCAT( RIGHT('----------',depth*3), menu_nm, ' ') AS parent_child_tree
FROM tree_view
ORDER BY path;


tree_view 조회 결과

menu_idmenu_nmup_menu_iddepthpathparent_child_tree
M00192메뉴/권한 관리M000013ROOT_M00001_M00192---------메뉴/권한 관리
M00190권한 관리M001924ROOT_M00001_M00192_M00190----------권한 관리
M00191메뉴 관리M001924ROOT_M00001_M00192_M00191----------메뉴 관리
M00193회사/조직 관리M000013ROOT_M00001_M00193---------회사/조직 관리
M00187회사 관리M001934ROOT_M00001_M00193_M00187----------회사 관리
M00188조직 관리M001934ROOT_M00001_M00193_M00188----------조직 관리
M00189사용자 관리M001934ROOT_M00001_M00193_M00189----------사용자 관리
M00194시스템관리M000013ROOT_M00001_M00194---------시스템관리


데이터를 tree 형태로 만드는 tree_test.ts 

- 조금 범용적으로 만들기 위해 menu_id -> id로 menu_nm -> name으로 , up_menu_id -> up_id 로 설계 했습니다.


필수 패키지 설치 및 vscode extension 설치

* 패키지 설치
$ sudo npm install -g typescript
$ sudo npm install -g ts-node    <누락되서 나중에 설치했어요>
$ mkdir tsTest
$ cd tsTest
$ npm init -y   #초기 프로젝트 설정
$ code .   # vscode 실행

* vscode extension 설치
$ Code Runner 설치하고 sample.ts 파이를 만들고, CTRL + ALT + N 하면 실행된다.
  한가지 아쉬운점은 jupyter notebook 처럼 변수 등등 디버깅을 모르겠다. ㅠ.ㅠ
   

      해당 디렉토리에 package.json 이 생긴다. $ npm install 을하면 package.json에 정의된 모듈들이 설치됩니다. 지금은 아무것도 없어서 package-lock.json 만 생기네요~


       * Code Runner 확장 프그램 설치

       



vscode에서 tree_test.ts 파일을 만든다

소스에는 원본 데이터 -> Tree 자료 구조 -> 출력 하는 함수가 포함되어 있습니다.

interface item {
  id: string;
  name: string;
  up_id: string | null;
}

interface TreeNode {
  id: string;
  up_id: string | null;
  label: string;
  avatar?: string;
  icon?: string;
  disabled?: boolean;
  children?: TreeNode[];
}

function buildTree(items: item[]): TreeNode[] {
  const menuMap = new Map<string, item>();
  const treeMap = new Map<string, TreeNode>();

  // 메뉴 항목 맵 만들기
  for (const item of items) {
    menuMap.set(item.id, item);
  }

  // 트리 노드 맵 만들기
  for (const [id, item] of menuMap) {
    const treeNode: TreeNode = {
      id: item.id,
      up_id: item.up_id,
      label: item.name,
    };

    treeMap.set(id, treeNode);

    // 부모 노드가 있는 경우, 부모 노드에 자식 노드 추가하기
    const parentId = item.up_id;

    if (parentId !== null) {
      const parent = treeMap.get(parentId);

      if (parent !== undefined) {
        if (parent.children === undefined) {
          parent.children = [];
        }

        parent.children.push(treeNode);
        console.log(treeNode);
      }
    }
  }

  // 최상위 노드들 찾기
  const rootNodes: TreeNode[] = [];

  for (const [id, item] of menuMap) {
    if (item.up_id === null) {
      const rootNode = treeMap.get(id);

      if (rootNode !== undefined) {
        rootNodes.push(rootNode);
        console.log(rootNode);
      }
    }
  }

  return rootNodes;
}

function printTree(treeNodes: TreeNode[], depth = 0) {
  for (const treeNode of treeNodes) {
    const indent = " ".repeat(depth * 2);
    console.log(`${indent}${treeNode.label} (${treeNode.id})`);

    if (treeNode.children !== undefined) {
      printTree(treeNode.children, depth + 1);
    }
  }
}

// 임의의 데이터 배열
const menuData = [
  { id: "ROOT", up_id: null, name: "ROOT" },
  { id: "M1", up_id: "ROOT", name: "메뉴 1" },
  { id: "M2", up_id: "M1", name: "서브메뉴 1-1" },
  { id: "M3", up_id: "M1", name: "서브메뉴 1-2" },
  { id: "M4", up_id: "ROOT", name: "메뉴 2" },
  { id: "M5", up_id: "M4", name: "서브메뉴 2-1" },
  { id: "M6", up_id: "M5", name: "서브메뉴 2-1-1" },
  { id: "M7", up_id: "M5", name: "서브메뉴 2-1-2" },
  { id: "M8", up_id: "M4", name: "서브메뉴 2-2" },
];

const tree = buildTree(menuData);
console.log(tree);

console.log("---BEGIN----------------------------------------------");
printTree(tree);
console.log("----END---------------------------------------------");

const menuData2 = [
  { id: "ROOT", name: "ROOT", up_id: null },
  { id: "M00001", name: "관리자", up_id: "ROOT" },
  { id: "M00192", name: "메뉴/권한 관리", up_id: "M00001" },
  { id: "M00190", name: "권한 관리", up_id: "M00192" },
  { id: "M00191", name: "메뉴 관리", up_id: "M00192" },
  { id: "M00193", name: "회사/조직 관리", up_id: "M00001" },
  { id: "M00187", name: "회사 관리", up_id: "M00193" },
  { id: "M00188", name: "조직 관리", up_id: "M00193" },
  { id: "M00189", name: "사용자 관리", up_id: "M00193" },
  { id: "M00194", name: "시스템관리", up_id: "M00001" },
  { id: "M00182", name: "공통코드 관리", up_id: "M00194" },
  { id: "M00183", name: "용어사전 관리", up_id: "M00194" },
  { id: "M00184", name: "로그 이력", up_id: "M00194" },
  { id: "M00235", name: "휴일 관리", up_id: "M00194" },
  { id: "M00236", name: "게시판 관리", up_id: "M00194" },
  { id: "M00237", name: "알림 관리", up_id: "M00194" },
  { id: "M00243", name: "사용자별 사용 현황", up_id: "M00194" },
  { id: "M00195", name: "화면 마법사", up_id: "M00001" },
  { id: "M00178", name: "조회 조건 관리", up_id: "M00195" },
  { id: "M00179", name: "메타정보 정의", up_id: "M00195" },
  { id: "M00180", name: "포틀릿 관리", up_id: "M00195" },
  { id: "M00181", name: "화면 마법사", up_id: "M00195" },
];

const tree2 = buildTree(menuData2);
// console.log(tree2);

console.log("---BEGIN----------------------------------------------");
printTree(tree2);
console.log("----END---------------------------------------------");


실행하기



     마우스 우 클릭을 하면 Run Code 가 나온다. 핫키는 Ctrol+Option +N 입니다.

실행 결과


Done] exited with code=127 in 0.01 seconds

[Running] ts-node "/Users/keunsookim/quasar/typescript/tree_test.ts"
{ id: 'M1', up_id: 'ROOT', label: '메뉴 1' }
{ id: 'M2', up_id: 'M1', label: '서브메뉴 1-1' }
{ id: 'M3', up_id: 'M1', label: '서브메뉴 1-2' }
{ id: 'M4', up_id: 'ROOT', label: '메뉴 2' }
{ id: 'M5', up_id: 'M4', label: '서브메뉴 2-1' }
{ id: 'M6', up_id: 'M5', label: '서브메뉴 2-1-1' }
{ id: 'M7', up_id: 'M5', label: '서브메뉴 2-1-2' }
{ id: 'M8', up_id: 'M4', label: '서브메뉴 2-2' }
{
id: 'ROOT',
up_id: null,
label: 'ROOT',
children: [
{ id: 'M1', up_id: 'ROOT', label: '메뉴 1', children: [Array] },
{ id: 'M4', up_id: 'ROOT', label: '메뉴 2', children: [Array] }
]
}
[
{
id: 'ROOT',
up_id: null,
label: 'ROOT',
children: [ [Object], [Object] ]
}
]
---BEGIN----------------------------------------------
ROOT (ROOT)
메뉴 1 (M1)
서브메뉴 1-1 (M2)
서브메뉴 1-2 (M3)
메뉴 2 (M4)
서브메뉴 2-1 (M5)
서브메뉴 2-1-1 (M6)
서브메뉴 2-1-2 (M7)
서브메뉴 2-2 (M8)
----END---------------------------------------------
{ id: 'M00001', up_id: 'ROOT', label: '관리자' }
{ id: 'M00192', up_id: 'M00001', label: '메뉴/권한 관리' }
{ id: 'M00190', up_id: 'M00192', label: '권한 관리' }
{ id: 'M00191', up_id: 'M00192', label: '메뉴 관리' }
{ id: 'M00193', up_id: 'M00001', label: '회사/조직 관리' }
{ id: 'M00187', up_id: 'M00193', label: '회사 관리' }
{ id: 'M00188', up_id: 'M00193', label: '조직 관리' }
{ id: 'M00189', up_id: 'M00193', label: '사용자 관리' }
{ id: 'M00194', up_id: 'M00001', label: '시스템관리' }
{ id: 'M00182', up_id: 'M00194', label: '공통코드 관리' }
{ id: 'M00183', up_id: 'M00194', label: '용어사전 관리' }
{ id: 'M00184', up_id: 'M00194', label: '로그 이력' }
{ id: 'M00235', up_id: 'M00194', label: '휴일 관리' }
{ id: 'M00236', up_id: 'M00194', label: '게시판 관리' }
{ id: 'M00237', up_id: 'M00194', label: '알림 관리' }
{ id: 'M00243', up_id: 'M00194', label: '사용자별 사용 현황' }
{ id: 'M00195', up_id: 'M00001', label: '화면 마법사' }
{ id: 'M00178', up_id: 'M00195', label: '조회 조건 관리' }
{ id: 'M00179', up_id: 'M00195', label: '메타정보 정의' }
{ id: 'M00180', up_id: 'M00195', label: '포틀릿 관리' }
{ id: 'M00181', up_id: 'M00195', label: '화면 마법사' }
{
id: 'ROOT',
up_id: null,
label: 'ROOT',
children: [ { id: 'M00001', up_id: 'ROOT', label: '관리자', children: [Array] } ]
}
---BEGIN----------------------------------------------
ROOT (ROOT)
관리자 (M00001)
메뉴/권한 관리 (M00192)
권한 관리 (M00190)
메뉴 관리 (M00191)
회사/조직 관리 (M00193)
회사 관리 (M00187)
조직 관리 (M00188)
사용자 관리 (M00189)
시스템관리 (M00194)
공통코드 관리 (M00182)
용어사전 관리 (M00183)
로그 이력 (M00184)
휴일 관리 (M00235)
게시판 관리 (M00236)
알림 관리 (M00237)
사용자별 사용 현황 (M00243)
화면 마법사 (M00195)
조회 조건 관리 (M00178)
메타정보 정의 (M00179)
포틀릿 관리 (M00180)
화면 마법사 (M00181)
----END---------------------------------------------

[Done] exited with code=0 in 0.932 seconds

마무리


이 ts 소스를 vuejs 에 넣어서 웹에 tree 형태로 출력하시면 됩니다.

typescript는 처음이라 함수를 재귀호출로 못만들었습니다. 람다등 좀더 공부를 많이해서
해봐야 겠습니다.

이만 클스 였습니다.


추가로 하는김에 vue에 붙혀서 웹으로 표시 해봤습니다. 추가적으로 클릭시 이벤트 받기, 아이콘 출력하기 tree customize 하기, drag & drop 하기... 





라벨: , , , , , ,