728x90
반응형

 

자바 개발자라면 API 호출은 일상 다반사입니다. 프론트엔드에서 주로 사용되는 API 호출 방식과 달리, 백엔드에서는 Java를 이용한 호출이 많이 이루어집니다. 특히 Apache HttpClient와 HttpURLConnection을 비교해볼 때, 각각의 장단점과 사용법을 알아보는 것이 중요합니다. 이 글에서는 두 라이브러리를 활용한 API 호출 방법을 자세히 설명하며, 왜 Apache HttpClient가 더 간결하고 효율적인지에 대해 탐구해보겠습니다.

 


 

HttpURLConnection
HttpURLConnection

 

HttpURLConnection을 이용한 API 호출

Java의 기본 라이브러리인 HttpURLConnection을 사용하면, 다음과 같은 방식으로 GET 요청을 보낼 수 있습니다.

 

HttpURLConnection을 이용한 기본 GET 요청 예제 코드

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

public class HttpGetRequestExample {
    public static void main(String[] args) {
        try {
            // 기본 URL
            String baseUrl = "http://example.com/api";
            
            // 파라미터 추가
            String params = "?param1=value1&param2=value2";
            String urlString = baseUrl + params;
            
            // URL 객체 생성
            URL url = new URL(urlString);
            
            // HttpURLConnection 객체 생성
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            
            // 요청 방식 선택 (GET)
            connection.setRequestMethod("GET");
            
            // 요청 헤더 추가 (옵션)
            connection.setRequestProperty("User-Agent", "Mozilla/5.0");
            
            // 응답 코드 가져오기
            int responseCode = connection.getResponseCode();
            System.out.println("GET Response Code :: " + responseCode);
            
            // 응답 코드가 200 OK인 경우, 응답 내용 읽기
            if (responseCode == HttpURLConnection.HTTP_OK) {
                BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
                String inputLine;
                StringBuffer response = new StringBuffer();
                
                while ((inputLine = in.readLine()) != null) {
                    response.append(inputLine);
                }
                in.close();
                
                // 응답 내용 출력
                System.out.println(response.toString());
            } else {
                System.out.println("GET request not worked");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

Apache HttpClient의 등장

 

반면, Apache HttpClient 라이브러리를 사용하면, 코드가 훨씬 간결해집니다. Maven을 사용하는 프로젝트의 경우, 다음과 같이 의존성을 추가해야 합니다.

 

Apache HttpClient를 이용한 GET 방식은 다음과 같습니다.

 

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version>
</dependency>

 

import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

public class HttpClientGetExample {
    public static void main(String[] args) {
        // HttpClient 객체 생성
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            
            // URIBuilder를 사용하여 URL 및 파라미터 구성
            URIBuilder uriBuilder = new URIBuilder("http://example.com/api");
            uriBuilder.addParameter("param1", "value1");
            uriBuilder.addParameter("param2", "value2");
            
            // HttpGet 객체 생성
            HttpGet httpGet = new HttpGet(uriBuilder.build());
            
            // 요청 헤더 추가 (옵션)
            httpGet.addHeader("User-Agent", "Mozilla/5.0");
            
            // 요청 실행 및 응답 받기
            try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
                // 응답 본문을 String으로 변환
                String result = EntityUtils.toString(response.getEntity());
                
                // 결과 출력
                System.out.println(result);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

 

 

Apache HttpClient를 사용하면 URIBuilder를 통해 URL과 파라미터를 쉽게 구성할 수 있고, HttpGet 객체를 생성하여 요청을 보낼 수 있습니다. 이는 코드를 간결하게 만들 뿐만 아니라, 개발자가 HTTP 통신의 세부사항에 덜 집중할 수 있게 해줍니다.

 


반응형

POST 요청 방식 비교

 

POST 요청에 대해서도 비교해봅시다. HttpURLConnection을 사용할 경우, OutputStream을 통해 직접 데이터를 전송해야 합니다.

 

HttpURLConnection을 이용한 POST 요청 예제 코드

 

import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Scanner;

public class PostExample {
    public static void main(String[] args) {
        String targetURL = "http://example.com/api/resource";
        HttpURLConnection connection = null;

        try {
            // URL 객체 생성 및 연결 설정
            URL url = new URL(targetURL);
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
            connection.setDoOutput(true); // OutputStream을 사용하여 요청 본문에 데이터를 쓸 것임을 지정

            // 요청 본문에 JSON 데이터 작성
            String jsonInputString = "{\"name\": \"John\", \"age\": 30}";
            try (OutputStream os = connection.getOutputStream()) {
                byte[] input = jsonInputString.getBytes("utf-8");
                os.write(input, 0, input.length);
            }

            // 응답 받기 및 출력
            try (Scanner scanner = new Scanner(connection.getInputStream())) {
                while (scanner.hasNextLine()) {
                    System.out.println(scanner.nextLine());
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }
}

 

 

반면, Apache HttpClient를 사용하면, 객체를 JSON으로 변환하여 HttpPost 객체에 전달하는 방식으로 간결하게 처리할 수 있습니다.

 

Apache HttpClient를 이용한 POST 요청 예제 코드

 

import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

public class JsonExample {
    public static void main(String[] args) throws Exception {
        // 객체 생성
        MyObject myObject = new MyObject();
        myObject.setName("John Doe");
        myObject.setAge(30);

        // 객체를 JSON으로 변환
        ObjectMapper objectMapper = new ObjectMapper();
        String json = objectMapper.writeValueAsString(myObject);

        // HTTP POST 요청 설정
        HttpPost post = new HttpPost("http://example.com/api");
        StringEntity entity = new StringEntity(json);
        post.setEntity(entity);
        post.setHeader("Accept", "application/json");
        post.setHeader("Content-type", "application/json");

        // 요청 실행
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            httpClient.execute(post);
        }
    }

    // 자바 객체 정의
    public static class MyObject {
        private String name;
        private int age;

        // getters and setters
        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
        public int getAge() { return age; }
        public void setAge(int age) { this.age = age; }
    }
}

 


 

 

Apache_HttpClient
Apache_HttpClient

마무리

이 글에서는 Java에서 API 호출을 위한 두 가지 방법, 즉 HttpURLConnection과 Apache HttpClient의 사용법을 비교해보았습니다. Apache HttpClient의 사용이 더 간결하고 유연하다는 것을 확인할 수 있었습니다.

Java 개발에 있어서 이러한 라이브러리의 선택과 활용은 프로젝트의 효율성을 크게 향상시킬 수 있습니다.

 

더 궁금한 점이 있으시다면 댓글로 질문해주세요. 감사합니다.

728x90
반응형
728x90
반응형

새 창을 열고 닫힐 때 특정 기능을 실행하는 방법 ?

웹 사이트를 개발하다 보면, 사용자의 편의성을 높이기 위해 새창을 열고, 해당창이 닫힐 때 특정 동작을 수행하고 싶을 때가 있습니다. 이번 포스팅에서는 이러한 상황을 'React'를 활용해 어떻게 구현할 수 있는지 알아보도록 하겠습니다.

 

먼저 기본이 되는 몇 가지 JavaScript 메소드에 대해 알아보겠습니다:

 

  • window.open() : 새 창을 여는 메소드로, 이를 통해 반환받은 창 객체 참조를 저장할 수 있습니다.
  • setInterval() : 주기적으로 코드를 실행하게 해주는 타이머 함수입니다.
  • windowObject.closed : 창이 닫혔는지 확인하는 속성으로, 창이 닫히면 true를 반환합니다.
  • clearInterval() : 기능을 사용하여 메모리 누수를 방지하려면 상위 구성 요소가 마운트 해제될 때 간격을 지워야 합니다

code_developer
code_developer

 


 

반응형

 


 

그럼 전체 코드를 한번 봅시다.

 

import React, { useEffect, useState } from 'react';

function ParentComponent() {
  const [childWindow, setChildWindow] = useState(null);

  // 하위 창이 닫힐 때 실행할 콜백 함수
  const onChildClose = () => {
    console.log('Child window closed');
    // 하위 창이 닫힌 후에 실행할 코드
  };

  // 하위 창을 여는 기능
  const openChildWindow = () => {
    const windowFeatures = 'width=600,height=400,resizable,scrollbars=yes,status=1';
    const childWin = window.open('/child-path', 'ChildWindow', windowFeatures);
    setChildWindow(childWin);
  };

  useEffect(() => {
    let intervalId;

    if (childWindow) {
      // 매초 자식 창이 닫혀 있는지 확인한다.
      intervalId = setInterval(() => {
        if (childWindow.closed) {
          clearInterval(intervalId);
          onChildClose();
        }
      }, 1000);
    }

    // 마운트 해제 시 Interval 해제
    return () => {
      if (intervalId) {
        clearInterval(intervalId);
      }
    };
  }, [childWindow]);

  return (
    <div>
      <button onClick={openChildWindow}>Open Child Window</button>
    </div>
  );
}

export default ParentComponent;

 

 

이 코드는 React의 useStateuseEffect 훅을 사용하여 새 창을 열고, 해당 창이 닫힐 때 onChildClose 함수를 실행합니다. setInterval 함수를 사용하여 1초마다 새 창의 상태를 확인하고, 닫힌 경우에는 clearInterval로 타이머를 해제한 후, 콜백 함수를 호출합니다.

 

이와 같이 구현하면 새 창을 열고 닫힐 때 원하는 기능을 실행할 수 있으며, 이는 웹 애플리케이션의 사용성을 향상시키는 데 도움이 됩니다. 오류나 더 좋은 구현 방법에 대한 의견이 있다면, 피드백을 주시기 바랍니다. 

 

 


 

 

 

IT 대모험 아직 끝나지 않았습니다.

언제나 공부하는 마음으로 새로운 언어, 새로운 환경에 적응해 나가길 바랍니다.

그럼 다음 또 다른 포스팅으로 찾아 뵙겠습니다~ 

728x90
반응형
728x90
반응형

 

이전 글 SI 개발자의 프로젝트를 선택할 때 고려사항에 대해

 

지극히 주관적인 필자의 생각으로  프로젝트를 선택할 때

 

무엇을 고려해야 할까 적어보았습니다 

 

궁금하시다면 아래의 링크를 클릭해주세요

 

2022.10.12 - [IT_Developer/About_Developers] - SI 개발자 프리랜서가 프로젝트를 선택할 때 무엇이 가장 중요할까?

 

SI 개발자 프리랜서가 프로젝트를 선택할 때 무엇이 가장 중요할까?

이전 글에서 개발자 등급별 단가에 대해서 급여 부분은 가장 민감하고 합당한 금액을 받아야 하기에 제 생각을 적어보았습니다 더 자세한 사항이 궁금하시다면 아래의 링크를 확인하세요 2022.09

tantangerine.tistory.com

 

 

하드코딩은 무엇일까?

 

개발자라면 다 알고 있으며 하드코딩이라는 이 단어를 정말 싫어할 것이다

 

왜 싫어하고 왜 하드코딩을 하는 것일까?

 

우선 하드코딩이란 무엇일까 사례를 살펴보면서 무엇인지 알아보자

 


 

하드코딩
하드코딩이란

 

 

비즈니스 업무에서 프로세스의 유연성이 없는 것이 하드코딩이다

 

비즈니스 업무에 프로세스를 진행함에 있어 유연성이 필연적이라고 할 수 있습니다

그 이유는 어떠한 행위를 하던 모든 것을 적용할 수 있어야 합니다

 

의류 쇼핑몰 플랫폼을 만들었다고 합시다

이 플랫폼은 에서 의류 매장을 연결하여 스마트 스토어처럼 판매한다고 합시다

 

그런데 이때 도매 사업 진출한다고 합니다

플랫폼에서 오프라인 매장에서는 납품받을 의류를 올리고

온라인 매장에서는 자신들이 판매할 의류를 올린다고 합니다

도매업은 플랫폼에 노출이 되지는 않지만 오프라인 매장에 의류를 납품하고 대금을 받아야 합니다

그래서 월별로 일정한 금액만큼 옷을 납품받기로 합니다

이 도매업의 비즈니스 업무를 추가한다고 한다면 

이 프로세스를 어떻게 추가해야 할까요??

 

매장별로 오프라인 매장과 온라인 매장을 구분하여

온라인 매장에 해당하는 의류만 플랫폼에 노출하고

오프라인 매장에 납품받을 의류들을 올리는 경우에는 노출하지 않게 하는 것입니다

그리고 이때 매장별로 상품을 적용하여 금액과 할인율을 다르게 표현하게 합니다

상품에 대한 매출이 발생하면 상품에 따라 정산을 진행하게 됩니다

 

이러한 프로세스를 정립하게 되었다면

오프라인 매장, 온라인 매장을 등록하고 매장별로 상품을 적용시켜 매출 정산을 진행합니다

이렇듯 유연성을 가진 이 프로세스는 플랫폼에서 고객들이 자유롭게 오프라인 매장을 등록할 때마다 

추가적인 작업이 없이도 업무가 가능할 것입니다

 

하지만 처음 온라인 매장만 있을 경우에서

프로세스를 정립을 하지 않고 개발자가 오프라인 매장을 추가할 경우 

추가할 오프라인 매장만 분기를 하여 그 매장만 노출을 하지 않는 쿼리를 새롭게 작성하여

오프라인 매장을 구성하게 된다면 정말 간단하고 빠른 시간 안에 완성할 수 있게 될 것입니다

 

하지만 이 경우에는 오프라인 매장이 추가될 때마다 분기를 추가해주어야 하며

담당자가 그만두거나 부재중이라면 어디서 추가를 하는지 알 수 없기 때문에

단 한 가지만이라도 하드 코딩된 곳을 찾지 못하고

추가를 못한다면 오류가 발생할 것입니다

 

이렇듯 하드코딩이 단순히 코드상 고정값이 들어가는 것이 문제가 아닌

어떠한 업무에 있어 프로세스를 정립할 경우

유연성이 없어 개발자가 추가해주어야만

프로세스가 진행되는 코드들이 하드코딩이라고 합니다 

 

 


 

이러한 문제점을 알면서도 왜 하드 코딩을 할까?

 

 

첫 번째는

정말 모르고 하는 개발자가 있다는 것입니다

필자도 한 번씩 왜 이렇게 코딩을 하느냐며,

프로세스를 정립하라고 조언을 합니다

그제야 그런 방법에 대해서 알게 되며 프로세스를 정립하기도 합니다

 

이것은 개발자가 비즈니스 업무에 대한 이해도가 없을 때 발생하는 현상입니다

프로젝트를 진행할 때 비즈니스 업무가 어떤 방식으로 진행되고 있는지

파악하는 것이 먼저이며 그것을 충실히 해야 하드코딩을 막을 수 있습니다

 

자신이 시작한 작은 하드코딩

후임 개발자는 큰 고난이라는 것을 명심하시고 개발하시길 바랍니다

 


 

두 번째는

위에도 잠깐 말했지만

귀찮거나, 시간이 없다는 핑계,

그 업무에 대해서 단발성으로 취급해버리기 시작하면서

시작된 작은 하드코딩업무가 점점 커져버리면서

걷잡을 수 없게 됩니다

 

운영을 하고 있다면 더더욱 변경하기 힘들어지며

한번 수정 시기를 놓치면 그것은 암흑의 동굴로 빠지게 될 것입니다 

 

누군가 시작한 하드코딩을 프로세스 화하기에는

운영 업무에서 사이드 이펙트가 엄청 크기 때문에 고치기 어려운 점이 있습니다

그래서 필자는 하드코딩을 보면 바로바로 개발자에게 수정 요청을 하며

필자가 개발한 코드에 누군가 하드코딩이 작성한다면 바로 수정합니다

 

자신이 작성한 코드에 자신의 이름을 기입하기에

저의 코드에 하드코딩이 있다는 것을 용납할 수 없습니다

 

이렇듯 마지막 자존심이라고 생각하시고

하드코딩을 하지 않기를 바랍니다

 


 

 

오늘은 하드코딩에 대해서 이야기해보았습니다

이것 또한 지극히 주관적인 이야기입니다

하지만 하드코딩은 좋은 것이 아니기에

하지 않는 것이 좋다는 것은 확실합니다

 

앞으로 좋은 마인드로 개발할 수 있도록 힘내시고

귀찮더라도 프로세스를 정립하고

시간이 없더라도 프로세스를 정립하는 것이

추 후에 시간을 아낄 수 있는 일이란 것을 알아 두셨으면 합니다

 

아직 IT 대모험은 끝나지 않았으니 힘내시고

더욱 정진할 수 있는 마음가짐을 갖도록 노력합시다

그럼 담에 또 봐요~

 

 

728x90
반응형
728x90
반응형

이전 글에서 개발자 등급별 단가에 대해서

 

급여 부분은 가장 민감하고 합당한 금액을 받아야 하기에

 

제 생각을 적어보았습니다

 

더 자세한 사항이 궁금하시다면 아래의 링크를 확인하세요

 

 

 

2022.09.05 - [IT_Developer/About_Developers] - IT 프리랜서 SI 개발자 초급, 중급, 고급 단가는 얼마나 될까?

 

IT 프리랜서 SI 개발자 초급, 중급, 고급 단가는 얼마나 될까?

이전 글에서 SI 프리랜서 개발자의 인력난에 대해서 최근 개발자 관심이 높아지면서 프리랜서의 정규직 전환이 많아졌습니다 그래서 프리랜서가 필요한 프로젝트에서는 인력난으로 단가도 높

tantangerine.tistory.com

 

 

 

SI 개발자가 프리랜서로 일할 경우에 프로젝트를 선택할 때 고려해야 하는 사항이 무엇일지

저만의 주관적인 생각을 적어보려 합니다

 

 

SI 개발자가 프리랜서로 일할 경우에 자신만의 프로젝트 선택 기준이 있을 것입니다

필자는 여러 가지 항목 중 단가, 비즈니스  업무, 개발 단계, 프로그래밍 언어, 프로젝트 환경 4가지 정도를 고려합니다

 

4가지에 대해서 왜 중요한지 말해 보도록 하겠습니다

 

 


 

첫째, 자신의 합당한 가치에 대한 단가

자신의 포지션에 합당한 대가를 받을 수 있는지에 대해서 생각합니다

고급이면 고급, 중급이면 중급의 단가가 책정되어있으며,

그 책정된 단가가 자신에게 합당한 지 잘 생각해보는 편입니다

 

물론 개발, 유지보수, 운영 별로 책정 기준이 다르며,

업무 강도가 얼마나 되는지도 단가 협의에 고려대상이 됩니다

 


 

둘째, 고급, 특급에 무엇을 할지 생각

SI 개발자는 일반 개발자와 다른 점이 여기에 있습니다

대기업을 상대로 어드민 사이트를 개발하다 보면

대기업의 주축 사업과 그 사업에 필요한 관리 업무를 프로세스화 하는 작업을 많이 합니다

업무는 물류, 정산, 인사, 회계, 은행, 보험 등 여러 가지가 있습니다

그에 따른 비즈니스 업무는 어느 정도 정형화되어 있으며

경험이 축적되면 여러 가지 요건에 대응하는 유연성도 높아지게 됩니다

 

그러기 위해서는 초급, 중급 때부터 고급, 특급이 되면 어느 비즈니스 업무를 할지

미리 생각해두고 공부하면서 준비를 해야 합니다  

 


 

셋째, 프로그래밍 언어의 중요성

프로그래밍 언어는 계속 변화해가고 있습니다

프런트 단은 vue.js, react.js 넥사크로 등

백단은 java, node.js 등

 java에서 사용하는 데이터 베이스 프로그래밍 프레임워크인 MyBatis 등

node.js에서 사용하는 express, sequelize 등

데이터 베이스 Oracle, MySQL 등

 

정말 수많은 프로젝트에서 여러 가지 언어들을 사용하고 있습니다

프로젝트를 하면서 여러가지 언어를 배우는 것도 좋고 실무경험을 쌓는 것도 좋기 때문에 고려대상이 될 수 있습니다

하지만 언어를 모른 채 들어가서 날코딩으로 작업을 해놓은 파일들을 보면

조금은 공부하고 가는 것이 좋을 것 같습니다

 

이것은 다음 포스팅에서 SI 프로젝트의 실태라는 주제로 글을 작성해보겠습니다

 


 

넷째, 자신의 워라벨을 위해

프로젝트 환경은 정말 중요합니다, 월차 사용할 수 있는 팀 분위기와 출퇴근시간, 야근 및 잔업 등

이런 것들은 뭐 설명을 안해도 다들 아실 것이라 생각합니다

 

 


 

이렇듯 필자는 여러 가지 요인들을 생각합니다

그중 단가와 비즈니스 업무가 무엇인지를 많이 보는 편입니다

 

단가를 협상할 경우에는 그 프로젝트 상황에 맞게 받는 것이 좋다고 생각합니다

운영 중인 서비스에서 개발업무가 많다면 개발업무를 하면서 운영 단가를 받게 된다는 것이며,

운영을 하면서 개발을 하는 것은 사이드 이펙트를 신경 써야 하는 부분이 많기에

쉬운 일이 아닙니다 그래서 다른 장점이 아니라면 선호하지 않습니다

그 이유는 개발 프로젝트에 투입해서 합당한 단가를 받아서 같은 업무를 할 수 있기 때문입니다

 

필자도 비슷한 상황이 있었습니다

지인의 소개로 프로젝트 면접을 보고 협상을 진행했습니다

운영 업무인지 모르고 진행되었던 면접이라 

면접 후 유선상으로 협상을 하는 과정에서 운영 단가로 책정되며

개발일이 너무 많아서 개발일도 할 수 있다며 장점을 내세우며,

개발환경도 신기술을 사용해서 배울 점이 많다는 점을 내세웠습니다

하지만 운영 단가를 책정하고 개발업무를 한다는 것이 장점이 될 수 있을까라는 생각을 하였고

면접 담당  PM이 운영 단가를 주고 개발을 하겠다는 마인드도 그렇게 좋게 받아들이기가 힘들었습니다

 

배울 점이 있다는 것은 좋은 요건이지만 

어느 프로젝트든 배울점이 많이 있으며 그것을 장점이라고 단가를 낮게 주겠다는 이야기는 

납득이 되지 않았습니다

 

 

그리고 두 번째로 비즈니스 업무 경험을 쌓기 위해 노력하고 있습니다

그 이유는 고급을 넘어 특급을 갈려고 한다면, 한 가지 업무를 집중해서 분석/설계까지 할 수 있는 능력을 보유해야 합니다

그러기 위해서는 웹 개편 고도화등 단순 개발 코딩 업무는 피하는 것이 좋다고 생각합니다

 

비즈니스 업무 경험을 쌓다 보면 대기업의 여러 가지의 요건을 경험하면서 대응력을 기르는 것이 중요하다고 생각합니다

SI 개발자 프리랜서는 고급과 특급으로 올라가려면 데이터 모델링이 필수라고 생각합니다

개발자로 남아서 개발을 하고자 하는 것도 좋지만

나이가 들고 계속 개발자로 일하는 것은 한계가 있다고 생각합니다

 

  하지만 지금 현재 나이가 많은 신 부장도 개발자로 일을 하는 현상이 보이기에

어느 것이 좋다고 말은 할 수 없지만

필자인 저는 모델링을 공부하는 것이 좋다고 생각하고 있습니다

 


 

프로젝트 선택할 경우 4가지 고려사항을 적어보았습니다

이것은 지극히 주관적인 필자의 생각입니다

정말 말도 안되는 이야기일 수도 있으며

다른 사람관점에서는 아니다라고 할 수도 있겠지요

 

그러니 자신의 생각을 댓글로 적어주시며 공유하였으면 합니다

그럼 오늘도 즐거운 하루되시길 바라며

우리의 IT 대모험을 계속되기를 ~

 

 

 

 

  

 

 

 

 

 

728x90
반응형
728x90
반응형

이전 글에서 주체가 되는 행위가 누락이 되면

 

하위 행위가 되는 데이터를 관리하기 힘들어지는 것을 알아보았습니다

그 밖에도 여러 가지의 단점들이 있었습니다

 

궁금하시다면 아래의 링크를 클릭해서 확인하시길 바랍니다

 

 

 

2022.09.26 - [Data_Modeling/Methodology] - 데이터 모델링 Account와 같은 상위 개체 집합인 행위 주체가 누락된다면?

 

데이터 모델링 Account와 같은 상위 개체 집합인 행위 주체가 누락 된다면?

이전 포스팅 업무 행위의 논리적 주체에 대해서 주체에 의해 행위가 이루어지는 업무들이 있다 그런데 행위가 주체로 이루어져 하위 행위들을 개체로 묶어서 사용할 경우도 있습니다 이러한 주

tantangerine.tistory.com

 


 

 

주 식별자로 데이터의 의미를 읽을 수 있다?

 

 

아래의 이미지를 보면

<그룹웨어 ID> 엔터티에서 <그룹웨어ID> 속성은 유일하게 관리됩니다.

<그룹웨어 ID>가 주 식별자 속성이기 때문입니다.

<사원>과 <그룹웨어 ID>는 1:N 관계이므로 한 사원은 여러 그룹웨어 ID를 사용할 수도 있습니다

하지만 <유효 종료일자가>가 지나서 사용이 만료된 경우라도,

즉 아무도 사용하지 않고 있더라도 다른 사원이 이 <그룹웨어 ID>를 재사용할 수 없는 구조입니다.

 

재 사용할 수 있다는 업무규칙을 지원하려면 M:N 관계가 되어 관계 엔터티를 추가로 식별해내야 한다.

 

아래의 이미지를 본다면 여러 사원이 같은 <그룹웨어 ID>를 동시에 사용할 수 있는 구조입니다
그것이 가능하다면 상관이 없지만 여러 사원이 사용을 못하도록 요건을 받았다면
모델링이 잘못되었다는 것을 알 수 있습니다

 

식별자의_의미
식별자가 있고 없고의 차이점

 

반응형

 

 

모델링을 어떻게 하느냐에 따라 관계도만으로 데이터를 어떻게 적재하는지 알 수 있습니다
그래서 잘못된 요건을 제대로 적용된 모델링을 해보도록 하겠습니다


<그룹웨어 ID> 엔터티를 자세히 보면 엔터티의 성격이 맞지 않다는 것을 느낄 수 있습니다
그 이유는 그룹웨어 ID라는 엔터티명와 칼럼명을 같이 사용하고 있으며, 


<유효 시작일자>와 <유효 종료일자> 속성은 계정의 본질적인 정보 아닌 
상품을 구입한 사람과 사용 행위에 종속된 속성이 더 맞다고 할 수 있습니다
그래서 아래와 같이 주체와 행위를 분리하여 정규화할 수 있습니다

 

 

식별자의_행위_주체
식별자의 행위와 주체를 구분하자



그리고 <그룹웨어 계정>과 <사원 그룹웨어 계정 사용>이 1:1 관계로 
사용 종료된 계정은 다른 사원이 사용할 수 없을 관계도만으로도 확인이 가능합니다

 

이러한 의미에서 식별자로 데이터가 어떻게 저장하게 되는지 그 구조를 파악할 수 있게 됩니다

 처음의 모델#1을 보면  여러 사원이 같은 <그룹웨어 ID>를 동시에 사용할 수 있는 구조입니다

 

고객사에서 어떠한 요건을 받아 의도한 바라고 한다고 해도

관리 측면에서 사이드 이펙트가 발생할 여지가 있습니다

그럼 고객사의 인터뷰를 통해 정말 필요한 요건인지 다시 확인하는 과정을 거쳐야 하며

정말 필요하다면 예상되는 사이드 이펙트를 해결해야 하는 방법을 제시해야 합니다

 

그리고 모델#1에서는 그룹웨어 ID가 존재하면서도 사원번호까지 주식별자로 사용하고 있습니다

이렇게 정의된 것은 현재의 구조에서는 의미가 없습니다

<그룹웨어 ID>만으로도 충분히 엔터티를 식별할 수 있으면서도

사원번호를 추가하는 것 슈퍼 식별자라고 하며 꼭 필요하지 않는다면

효율면에서도 떨어지니 남용하지 않는 것이 좋습니다

 

또한, 부적합한 데이터가 쌓일 수 있는 구조를 만드는 빌미를 제공하며,

엔터티의 인스턴스가 늘어나는 기준을 불명확하게 만들어 모델의 가독성을 훼손하기도 합니다

 

 

프로젝트 성패를 결정짓는 데이터 모델링 이야기 中 김상래 지음


 

 

오늘은 주 식별자에 대해서 알아보았습니다

주 식별자는 모델링에서 많이 보는 형태이면서

아무렇지 않게 남용하는 경우도 보셨을 것입니다

이것을 어느 상황에서 써야 할지 모르고 그냥 연결을 하기 위해 사용하는 것은

아닌지 하는 생각을 했습니다

 

한 번쯤 깊게 고민해서 노트에 정리해야 할 듯합니다

 

아무튼 오늘도 한걸음 나아가는 보람찬 하루였음을 생각하며

우리 모두 IT 대모험을 이겨내 봅시다

 

728x90
반응형
728x90
반응형

이전 포스팅 업무 행위의 논리적 주체에 대해서

 

주체에 의해 행위가 이루어지는 업무들이 있다

그런데 행위가 주체로 이루어져 하위 행위들을 개체로 묶어서 사용할 경우도 있습니다

 

이러한 주체를 찾지 못한다면 비즈니스적으로 유연함이 부족한 모델링이 될 수 있습니다

그래서 그 관점을 풍부하게 해 줄 필요성이 있습니다

 

더 자세한 사항을 알고 싶다면 아래의 링크를 클릭해주세요!

 

 

 

2022.09.23 - [Data_Modeling/Methodology] - 데이터 모델링 업무 행위의 논리적 주체란 무엇일까?

 

데이터 모델링 업무 행위의 논리적 주체란 무엇일까?

이전 포스팅에서 최상위 데이터에 해당하는 마스터 데이터에 대해서 마스터 데이터란 기업이나 조직 활동의 근간을 이루는 데이터로서 조직에서 발생하는 데이터는 육하원칙에 범주 하는 기준

tantangerine.tistory.com

 


 

 

Account와 같은 상위 개체 집합이 누락된다면

 

Account는 마스터 데이터에서 비즈니스 업무에서 행위가 주체가 되는 상황에서

Account라고 칭하며 그 의미를 제대로 이해해야 합니다

그 데이터는 <고객> 엔터티와 고객의 행위 업무인 <주문> 엔터티 나타나게 됩니다

 

이때 아래와 같이 주문 엔터티에 <주문번호>, <주문 상품번호>까지 포함된다면 행위가 발생할 경우 

관리의 단위는 주문한 상품이다라는 것을 알 수 있습니다

 

데이터_모델링_행위의주체
주체가 없는 데이터 테이블의 구성도

 

그 이유는 주문이라는 행위가 발생하면

하나의 주문번호에 여러 개의 주문 상품번호가 연결이 되지 않아

고객이 3개의 상품을 한 번에 주문했더라도 개체는 3개가 만들어지는 구조입니다

 

개별 주문 상품을 묶는 단위로서

<주문> 엔터티를 어떻게 만들고

<주문 상세> 엔터티를 생성해서

주문 상품을 연결하는 방법을 생각해야 합니다

 

행위의 주체가 되는

<주문> 엔터티 <주문 일자>, <주문 배송지 주소>, <고객번호>

주문생성에 필요한 정보들을 구성해주고

행위가 되는 하위 데이터의 <주문 상세> 엔터티는 주문한 상품에 대한 정보를 넣어주거나

 <상품 시작일자>, <상품 종료일자>, <주문 상태>를 구성할 수도 있습니다

 

 

반응형

 

주문을 대표하는 개체가

주문 취소나 환불, 부분 취소 등의 프로세스로 발생하여

주문 상품 정보가 변경되면 비즈니스의 유연성이 좋아야 합니다

 

이번 사례는 정말 간단하고 주문이라는 익숙한 업무이기에

문제점이 눈에 확연히 보일 것입니다

하지만 프로젝트를 수행할 때 물류, 정산, 인사 등

여러 가지 비즈니스 업무를 할 경우 업체에서 주어지는 요건에 새로운 행위가 추가된다면

신경 써서 요건을 수립해야 할 것입니다

 

즉, 행위가 발생할 때 그 행위가 하나의 행위로 끝이 날지?

행위를 묶을 상위의 행위 주체가 필요할지?

항상 기억하고 생각하여야 한다는 것입니다

 

지금 프로젝트를 진행할 때 주문이 아닌 어떤 다른 행위가 일어날 때 

어떻게 데이터를 형성하는지?

행위를 할 때마다 데이터를 형성하는 것인지?

그 경우 데이터 관리의 단위가 어떤 것인지?

행위의 주체가 필요한지? 

행위를 묶어서 관리가 필요한지? 등

여러 가지를 생각해보아야 합니다

 


 

이렇게 오늘도 모델링에 대해 조금더 알아보았습니다

 

행위가 주체가 되어 하위 행위들을 집단으로 묶는 경우가 발생할지

또 다른 관점으로 데이터를 봐야 할듯합니다

 

물론 이런 것들을 이해하기 힘들것같습니다

 

하지만 행위가 주체가 될 수 있다는 점만 알고 있다면

다음 모델링을 하게된다면 조금은 다른 사람들보다

특별한 관점으로 번뜩이는 것이 있지않을까 생각합니다

 

그럼 파이팅 하시고 IT대모험을 즐겁게 즐기도록!! 하시길 바랍니다

Account 꼭 기억하세요!

728x90
반응형
728x90
반응형

이전 포스팅에서 최상위 데이터에 해당하는 마스터 데이터에 대해서

 

마스터 데이터란 기업이나 조직 활동의 근간을 이루는 데이터로서

조직에서 발생하는 데이터는 육하원칙에 범주 하는 기준 정보를 부모로 표현합니다

 

그리고 기준에 따라 행위가 주체가 되는 경우도 알아보았습니다

 

이전 포스팅이 궁금하시다면 아래의 링크를 클릭해주세요

 

그럼 이 처럼 중요한 마스터 데이터를 논리적 주체에 의해 다시 알아보겠습니다

 

 

2022.09.20 - [Data_Modeling/Methodology] - 데이터 모델링 최상위 데이터에 해당하는 마스터 데이터란 무엇일까?

 

데이터 모델링 최상위 데이터에 해당하는 마스터 데이터란 무엇일까?

이전 포스팅에서 데이터를 읽으려면 데이터 유형 혹은 패턴에 대해서 데이터 유형을 알아야 데이터를 잘 읽을 수 있습니다 일반적으로 데이터에는 그 성격이 유사해서 하나의 틀로 묶을 수 있

tantangerine.tistory.com

 

 

 

Account라고 하는 마스터 데이터란 무엇일까

 

업무 행위의 최상위 주체로 관련 업무 처리들을 동일한 성격으로 관리하는 단위입니다

또한 수많은 행위 데이터를 동일한 성격으로 묶을 수 있는 단위 개체입니다

일반적으로 예약 행위를 통해 생성되며, 다른 하위 행위의 직접적인 주체 역할을 담당합니다

 

즉, 행위 데이터의 주체가 되어 의미를 나타냅니다

 

행위 주체라는 것은 행위가 해당 주체에 완전 종속되었음을 의미합니다

그래서 Account를 어떤 업무 처리 데이터들을 동일한 범주로 관리할 수 있는 단위입니다

 

그런 행위의 주체가 되는 것이 무엇이 있는지

예를 살펴봅시다

 

어느 CC웹 사이트에서 AA라는 사람 BB일반 회원, CC상점 회원 2개의 계정을 만들 수 있다고 합시다

그렇게 회원을 만들려면 물리 개체인 사람과 논리 개체로서의 회원을 분리해야 합니다

 

이때 AA라는 사람이 CC웹 사이트 활동하는 BB일반 회원, CC상점 회원 계정은 AA라는 물리적 정보가 아닌

BB일반 회원, CC상점 회원이라는 논리적인 계정을 최상위가 된다는 것을 알아야 합니다

 

 

또 다른 예로 엄마, 아빠 아들로 구성된 가족은

모두 같은 이동통신사의 서비스를 이용한다고 해봅시다

 

엄마는 본인이 사용한 서비스와 아들의 서비스 비용을 하나의 청구 단위로 지불할 것을 요청받습니다

즉, 계약 단위가 아닌 계약의 묶음 형태로 청구가 이루어지고 있습니다

그때 <청구계정> 엔터티가 필요합니다

하지만 청구 계정 엔터티가 없다면 이렇게 묶음 서비스를 할 수가 없을 것입니다

 

하지만 이러한 개념은 알고 있는 사람들이 많을 것입니다

<주문> 엔터티라는 행위의 주체가 되며 

<주문 상세> 엔터티의 관계가 그 사례입니다

 

이것은 가장 일반적인 구조의 ERD입니다

그래서 비즈니스 업무를 파악할 때 이경우를 제외하고

다양한 형태의 행위의 주체가 되는 것을 찾을 수 있을 것입니다

 

업무 행위의 주체를 정확하게 식별하고,

업무 서비스의 최상위에 해당하는 Account를 명확하게 정의하기 위해서는

자신만의 안목과 기준을 구축해가는 시간이 필요합니다

 

 

프로젝트 성패를 결정짓는 데이터 모델링 이야기 中 김상래 지음

 


 

업무의 행위 주체가 되어 다른 하위 행위의 직접적인 주체가 되는 경우가 

어떤 것이 있을지 주문, 주문상세 이외에는 떠오르지 않습니다

그래서 Account를 계속 생각하며

실무적인 안목을 키워야 할 것 같습니다

 

정말 데이터 모델링은 어렵고 힘든 작업인 것 같습니다

다음에 이 책으로 공부를 끝내고

어떤 서비스 웹 사이트를 보고 

데이터 모델링하는 과정을 포스팅하도록 하겠습니다

하지만 이 책을 언제 다 볼지 잘 모르겠네요

 

아무튼 오늘도 이렇게 좋은 한걸음 나아갔습니다

공부는 꾸준히 하시고 다 함께 힘내시길 바랍니다

 

우리의 IT 대모험은 끝나지 않았으니까요 

728x90
반응형
728x90
반응형

이전 포스팅에서 express의 미들웨어와 라우터 차이점에 대해서

 

미들웨어는 서버와 클라이언트 간의 중간 매개 역할을 담당합니다

그래서 서버 api 요청할 경우 미들웨어에서 공통업무를 할 수 있게 합니다

에러 페이지, 존재하지 않는 페이지 등

 

조금 더 상세한 정보를 알고 싶으시면 아래의 링크를 확인해주시길 바랍니다

 

 

 

2022.09.16 - [IT_Web/Nodejs] - Node express 서버의 미들웨어, 라우터 개념 제대로 알기 및 next함수 활용하기

 

Node express 서버의 미들웨어, 라우터 개념 제대로 알기 및 next함수 활용하기

이전 포스팅에서 express 서버 미들웨어 적용에 대해서 미들웨어가 무엇이며, 미들웨어를 어떻게 활용하는지에 대해서 알아보았습니다 미들웨어를 사용함에 있어 라우팅이 호출될 때 공통적으로

tantangerine.tistory.com

 


 

Express 라우터 url 파라미터 및 쿼리스트링 컨트롤 하기

 

라우터에서 서버 api요청 시 url의 파라미터와 쿼리 스트링을 제어할 수 있습니다

 

이 방법은 페이지를 이동하거나

서버에 요청하여 통신을 할 때 필요한 값을 전달할 때 자주 사용합니다

 

 


 

파라미터(parameters)란

 

번역하면 매개 변수라고 하며

url의 주소에 어떠한 특정한 값을 변수에 담아 유동적으로 

변화하는 값을 사용합니다

그래서 아래와 같이 '/top/:page'를 사용하면

/top/ 이후의 값은 변수에 담겨 라우터에서 사용이 가능합니다

 

 


 

쿼리스트링(Query String)

 

Query만 번역하면 의문,

String 컴퓨터 자료형으로 해석하면 문자를 의미합니다

그래서 이 두 가지를 합치면 의문 문자라고 해서

'/top/:page?id=컴퓨터&name=삼성'

위의 방식으로 의문(?)부터 시작된 문자를 쿼리 스트링이라고 합니다

 

 


코드 분석

 

그럼 코드를 보면서 나머지를 설명하겠습니다

아래와 같이 코드를 작성하고 npm start를 시작합니다

 

npm init, npm i express, npm i -d nodemon 등 설치는 

이전 포스팅을 찾아보시면 나와있으니 확인 부탁드립니다

 

 

const express = require('express');

const app = express();
app.set('port', process.env.PORT || 3000);

app.get('/top/:page', (req, res, next) => {
  console.log('top params: ', req.params)
  console.log('top queryString: ', req.query)
  res.send(`top: ${req.params.page} / id: ${req.query.id} name: ${req.query.name}`)
});

app.listen(app.get('port'), () => {
  console.log(app.get('port'), '번 포트에서 대기 중');
});

 

 

 

위와 같이 실행하면 아래와 같이 이미지가 브라우저 및 콘솔 창에 노출됩니다

http://localhost:3000/top/head를 주소창에 입력했을 경우 

console.log함수에 노출되는 정보를 확인할 수 있습니다 

 

 

 

express_파라미터_쿼리스트링_1
express_파라미터_쿼리스트링 브라우저 확인

 

 

req.params, req.query로 접근해서 파라미터와 쿼리스트링을 가져올 수 있습니다

그리고 undefined가 발생하는 값을

브라우저 주소창에 http://localhost:3000/top/head?id=컴퓨터&name=삼성

입력하게 된다면 id, name 값이 할당될 것입니다

그 값은 아래의 이미지에서 확인할 수 있습니다

 

 

express_파라미터_쿼리스트링_2
express_파라미터_쿼리스트링 데이터

 

 

위의 데이터를 확인하고 아래와 같이 노출할 수 있는

코드를 작성할 수 있습니다

 

 

express_파라미터_쿼리스트링 브라우저 노출확인

 

 


이렇게 오늘은 파라미터와 쿼리스트링을 활용하는 방법을 알아보았습니다

 

그렇게 어렵지 않으며 nodeexpress 말고도 자바에서도 비슷한 방식으로

사용하고 있는 것만 알고 계시면 될 것 같습니다

 

하루에 1포스팅을 실천하기 위해 

공부도 나름 동기부여도 되는 것 같습니다

 

그럼 같이 파이팅 하시고 힘내세요!

우리의 IT 대모험 멀고도 머니깐요

 

 

728x90
반응형
728x90
반응형

이전 포스팅에서 데이터를 읽으려면 데이터 유형 혹은 패턴에 대해서 

 

데이터 유형을 알아야 데이터를 잘 읽을 수 있습니다

일반적으로 데이터에는 그 성격이 유사해서 하나의 틀로 묶을 수 있는 유형이 존재합니다

 

그것을 제대로 파악해서 업무 정보는 데이터를 육하원칙에 따라 결합한 체계를 파악할 수 있습니다

 

더 상세한 정보를 알고 싶으시다면 아래의 링크를 클릭해주세요

 

 

 

2022.09.19 - [Data_Modeling/Methodology] - 데이터 모델링 중 데이터를 읽으려면 데이터 유형 혹은 패턴 제대로 알자

 

데이터 모델링 중 데이터를 읽으려면 데이터 유형 혹은 패턴 제대로 알자

이전 글에서 엔터티 모델링이 어려운 이유에 대해서 데이터 집합을 정의하기는 쉽지 않고 데이터 본질을 파악하기도 어렵습니다 그래서 무엇보다 모델링 마인드가 필요하며 다른 많은 이유들

tantangerine.tistory.com

 

 

 

마스터 데이터란

 

기업 활동의 핵심이 되어 자주 사용되는 데이터로 정의하는 곳도 있습니다

하지만 모델링에서 마스터 데이터는 그렇게 표현하면

데이터의 성질에 맞지만 자주 사용된다는 것은 옳지 못한 표현입니다

 

그래서 "기업이나 조직 활동의 근간을 이루는 데이터"라고 하는 것이 적당할 것입니다

그리고 무엇보다 활동의 근간이라는 표현에 주목해야 합니다

 

활동 자체의 데이터는 마스터 데이터가 아니며 활동의 근간 즉 기준이 되는 데이터여야 한다는 것입니다 

그리고 상위 모델을 체계화하지 않은 채 하위 모델을 제대로 조직한다는 것,

결국 관리해야 할 데이터를 정확하고 효율적으로 담을 수 없게 됩니다

 

 

마스터 데이터를 생각하여 유형 관점을 찾고 모델링하자

 

모델이 다단계의 부모-자식 관계를 갖는 이유는

업무 행위가 다른 업무 행위의 주체가 될 수도 있고, 논리적인 개념 역시 상위 집합에 위치할 수 있기 때문입니다

 

 

데이터_모델링_마스터_데이터
유형 관점에서 정리한 데이터 모델

 

 

위처럼 다단계 계층을 이루게 되므로,

모델링할 때나 기존 모델을 분석할 때는 족보의 뿌리를 찾아가듯

최상위의 행위자와 행위의 대상을 명확히 정의하고 식별해야 합니다

 

운영 중인 데이터베이스에서 데이터 모델을 추출하는 리버스 모델링에도 그대로 적용됩니다.

 

1. 최상위의 고객과 상품을 찾는다.

 

2. 고객과 상품 수준의 다른 주체와 대상도 찾는다.

 

3. 고객과 상품이 행한 업무 트랜잭션(주로 계약 수준)을 찾는다.

 

4. 트랜잭션이 주체로 행한 하위 트랜잭션을 찾는다.

 

5. 관계를 찾아 연결한다.

 

6. 각 엔터티를 주요 속성부터 채워나간다.

 

7. 이력 등 기타 엔터티를 고려하며 전체 모델을 상세화한다.

 

 


 

오늘은 마스터 데이터를 파악하고

그 근원적인 데이터로 다단계층을 구조를 구성하여

 최상위 행위자와 행위의 대상을 명확히 구분 지어야 합니다

 

아직까지 모델링이란 것이

명확하게 할 수 있다고 할 수 없습니다

 

계속 공부하고 계속 테이블 설계도를 계속 보면서 눈으로 익혀야 합니다

 

분석/설계 업무를 할 수 있는 수준까지 올려서

한 단계 올라설 수 있게 노력해야 할 듯합니다

 

그럼 오늘도 파이팅 하시고

항상 공부하는 습관을 갖도록 노력합시다!

 

우리의 IT 대모험은 아직 끝나지 않았으니까요!!

 

 

728x90
반응형
728x90
반응형

이전 글에서 엔터티 모델링이 어려운 이유에 대해서

 

데이터 집합을 정의하기는 쉽지 않고 데이터 본질을 파악하기도

어렵습니다 그래서 무엇보다 모델링 마인드가 필요하며

 

다른 많은 이유들로 모델링이 어렵습니다

 

조금 더 상세한 이유가 필요하시다면 아래의 링크를 클릭해주세요

 

2022.09.13 - [Data_Modeling/Methodology] - 데이터 모델링 중 엔터티 모델링이 어려운 이유는?

 

데이터 모델링 중 엔터티 모델링이 어려운 이유는?

이전 글에서 데이터 모델링이 철학적 관점이 필요한 이유에 대해서 비즈니스 업무를 데이터적 관점으로 보는것은 매우 어려운일이다 그것을 서양 철학적 관점으로 해석으로 데이터 관점을 새

tantangerine.tistory.com

 


 

데이터를 읽으려면  데이터 유형 혹은 패턴 제대로 알자

 

일반적으로 데이터에는 그 성격이 유사해서

하나의 틀로 묶을 수 있는 유형 혹은 패턴이 존재함을 알 수 있습니다

 

아래의 두 사건을 놓고 생각해 봅시다

 

A가 S전자의 상품 X를 2015년 10월 17일에 구매했다.

B가 K은행의 계좌 Y에 2015년 10월 18일 15시 27분경 100만원을 입금했다

 

문장에 주어에 해당하는 구매와 입금에는 행위의 주체인 A와 B가 존재합니다

그리고 이들 행위의 대상인 목적어는 상품 X와 계좌 Y도 존재합니다.

 

구매와 입금이라는 행위 자체와 행위가 일어난 시각도 물론 확인할 수 있습니다

 

이렇듯 비즈니스 업무에 해당하는 주체가 어떤 행위를 발현할 경우

육하원칙에 따라 엔터티가 구성될 수 있습니다 

그러므로 육하원칙을 생각하며 그에 상응하는 구조적 체계를 잘 생각해보아야 합니다

 

업무 데이터는 업무와 관련된 사건의 기록이며, 

업무 정보는 데이터를 육하원칙에 따라 결합한 체계라고 말할 수 있습니다

 

업무 요건을 어떻게 데이터 모델로 표현할 것인가?는

아래의 이미지를 보면 명쾌한 답이 될 수 있습니다

 

데이터_모델링_유형
업무요건 형성화한 데이터 모델

 

성격이 같은 데이터는 하나의 유형으로 통합하고

업무 행위는 관련 개체들과의 관계로 표현하는 것이 핵심입니다

 

즉 비즈니스 업무들이 어떤 업무들이 발현되고

그 업무들의 유사성을 찾아 유형/코드를 생성합니다

그리고 그에 따라 관련된 업무가 어떤 식으로 행위가 이어지는지 확인해야 합니다 

 

특히 업무 트랜잭션(행위) 성격의 엔터티를 검증할 때 해당 엔터티와

육하원칙 관계에 있는 주변 엔터티들이 모두 제대로 된 관계로 연결되어 있는지 확인합니다

 

데이터 사이에는 종속관계와 같은 구조적 특성도 발견됩니다

개별 데이터의 성격에서 구현됩니다

 

예를 들어 대출 상환 업무에서 상환 금액과 상환 일시 데이터는

대출이라는 행위 데이터에 종속될 수밖에 없습니다

대출이라는 업무가 발생하지 않는다면 대출 상환은 절대 존재할 수 없기 때문입니다

 

중앙 <비즈니스> 엔터티는 주변의 '누가', '무엇을', '언제', '어떤 유형' 등의 데이터에 종속됩니다

주변의 기준 정보가 존재하지 않으면 업무 행위 자체가 발생할 수 없습니다

 

즉, 부모 역할을 하는 주변 개체들에 대해 깊이 살펴볼 것입니다

 

트랜잭션의 실질적인 주체와 대상에 해당하는 데이터를 정확히 볼 줄 알아야 합니다

 

일반적으로 데이터 아키텍처와 방법론에서는 이러한 데이터를 마스터 데이터라고 하며

중요하고 일관되게 관리되어야 합니다

 

업무 행위와 행위의 주체/대상

업무의 주체나 대상 등 같은 성질의 데이터를 하나의 개체로 통합하고,

업무행위는 관련 개체 사이의 관계로 표현하는 것이 핵심입니다

 

 

프로젝트 성패를 결정짓는 데이터 모델링 이야기 中 김상래 지음

 

 


 

 

데이터를 잘 읽으려면 비즈니스 업무 요건을 

충분히 이해를 하여 데이터 모델링을 해야 합니다

 

그러기 위해서는 주체를 알고 육하원칙에 맞게 엔터티를 구성해야 합니다

그 구성에 따라 업무에 대한 행위를 다시 생각해보며

그 부모 객체가 누구인지 누구에 의해 발현되고 있는지 확인해야 합니다

 

육하원칙을 어떤 식으로 파악하고 적용할지에 대한 것들도

업무를 얼마만큼 경험했는지가 중요한 요소인 것 같습니다

 

물류, 정산, 회계, 인사 등

여러 가지 ERP 업무들이 존재합니다

하지만 그것 또 한 회사마다 다릅니다

그래서 모든 비즈니스 업무를 다 파악할 수 없으며, 현업에 종사하는 사람들도

어떤 업무들이 추가되며 발생할지 예상을 못할 수 있습니다

그렇기 때문에 모델링에서는 모든 업무들을 범용성이 있게 구성하는 것이 가장 이상적이지 않을까 합니다

 

데이터 모델링을 계속 공부하면서도 부족함을 느낍니다

그래서 다른 교육기관을 가볼까 하고 생각 중입니다

 

아무튼 배움은 중요하니깐요

모두 힘내시고! 파이팅 하시길 바랍니다

 

 

 

 

 

 

 

 

 

 

 

728x90
반응형
728x90
반응형

이전 포스팅에서 express 서버 미들웨어 적용에 대해서

 

미들웨어가 무엇이며, 미들웨어를 어떻게 활용하는지에 대해서 알아보았습니다

미들웨어를 사용함에 있어 라우팅이 호출될 때 공통적으로 처리하거나

에러 미들웨어를 만들어 에러 발생 시 특정 함수를 실행하는 등

공통적인 부분을 담당하고 있는 미들웨어를 적용해보았습니다

 

더 상세한 정보를 알고 싶으시다면 아래의 링크를 확인해보세요

 

 

2022.09.15 - [IT_Web/Nodejs] - Node에서 express 서버 미들웨어 적용하기 그리고 middleware를 쓰는 이유가 뭘까??

 

Node에서 express 서버 미들웨어 적용하기 그리고 middleware를 쓰는 이유가 뭘까??

이전 포스팅 express 패키지 활용하여 서버 구축 및 html 서빙에 대해서 express에서 html 서빙 방법은 정말 간단합니다 express 내에서 html관련 패키지를 사용하고 있어 개발자들은 단지 메서드를 호출

tantangerine.tistory.com

 

 

express에서 미들웨어와 라우터 개념 제대로 알기

 

다른 개발자 분들은 use함수가 미들웨어가 아니라

get 함수 등(이외에도 많은 것들이 있습니다 post 등)

함수 내의 콜백 함수가 미들웨어라고 말하시는 분들이 있습니다

 

충분히 그렇게 생각할 수도 있지만

제 생각은 조금 다릅니다

 

미들웨어는 use 함수이며

라우터는 get 함수가 맞다고 생각합니다

 

그 이유는 미들웨어 격인 use 함수는 

라우터 함수가 호출될 때를 감지해서 우선 실행되는 함수입니다

클라이언트와 서버 간에 중간 매개 역할을 하여 컨트롤이 가능합니다

그래서 use 함수 자체가 미들웨어라고 할 수 있습니다

 

그리고 next 함수는 라우터와 미들웨어에서 편의성을 위한 기능일 뿐이라는 것입니다

 

그리고 엄연히 따지면 get 함수도 라우터가 아닌 서버 api 호출 문으로도 해석이 가능합니다

하지만 지금 현재 브라우저 주소 url서버의 연동하여 페이지를 전송받기 때문

라우터라고 부르는 것이 더 합리적일 것입니다 

 

 

 

express 서버 미들웨어와 라우터에 있는 콜백 함수 next 활용법

 

콜백 함수 next 함수를 호출하면

 

최우선 호출되는 것은 자신의 또 다른 콜백 함수가 호출됩니다

하지만 자신의 콜백 함수를 호출하지 않고 다음 미들웨어나 라우터 함수를 호출하기 위해서는 

next('route')로 인자 값을 'route'로 입력하는 것입니다

 

하지만 이때 미들웨어인 use함수는 다음 미들웨어나 라우터 함수를 호출하는 것이 아닌

자신의 콜백 함수를 다시 호출한다는 점이 미들웨어와 라우터가 다른 기능입니다

 

* 미들웨어에서 next('route')를 호출하면 자기 콜백 함수를 우선 호출하고

그다음 라우터를 실행하게 됩니다 

 

 

그럼 아래의 코드를 실행해 봅시다

 

 

const express = require('express');
const path = require('path');

const app = express();
app.set('port', process.env.PORT || 3000);

app.use((req, res, next) => {
  try {
    console.log('express server: use ::: 1')
    next('route')
  } catch (error) {
    console.log('express server: error ::: use ::: 1', error)
    next(error)
  }
}, (req, res, next) => {
  console.log('express server: use ::: 2')
   next()
})

app.get('/', (req, res, next) => {
  try {
    console.log('express server: main ::: 1')
    if(true){
      next('route')
    } else {
      next()
    }
  } catch (error) {
    console.log('express server: error ::: main ::: 1', error)
    next(error)
  }
}, (req, res, next) => {
  console.log('express server: main ::: 2')
  next()
})

app.get('/', (req, res, next) => {
  try {
    res.json({ page: 'main'})
    console.log('express server: main ::: 3')
  } catch (error) {
    console.log('express server: error ::: main :: 3', error)
    next(error)
  }
});

app.get('/top', (req, res, next) => {
  try {
    console.log('express server: top ::: 3')
    res.json({ page: 'top'})
  } catch (error) {
    console.log('express server: error ::: top', error)
    next(error)
  }
});

app.use((req, res, next) => { 
  res.status(404).send('존재하지 않는 페이지 입니다(404)')
});

app.use((error, req, res, next) => {
  res.send('error 발생')
});

app.listen(app.get('port'), () => {
  console.log(app.get('port'), '번 포트에서 대기 중');
});

 

 

그럼 아래처럼 화면이 노출됩니다

 

미들웨어에서 next()를 사용해서 use ::: 1, use ::: 2 함수가 실행되며

그다음 main ::: 1 함수가 실행되고

main ::: 2는 건너뛰고

 main ::: 3 함수가 실행됩니다

 

 

 

 

이렇게 미들웨어와 라우터 제어하는 함수를 알아보았습니다

next함수 활용법과 라우터와 미들웨어 둘의 차이점을 알아보았습니다

 

라우터와 미들웨어에 존재하는 next함수에 대해 이야기하고 넘어가야겠습니다

express 공부한 지 얼마 안 되었지만

 

그렇게 좋은지 알 수가 없었습니다

 

next함수만 보더라도 라우터 get함수의 콜백 함수 next와 

미들웨어 use함수의 콜백 함수 next는 활용방식이 너무나도 다릅니다

 

use함수 내에서는 next('route') 통하지 않고

자기 내부의 콜백 함수를 다시 호출하게 됩니다

 

이렇게 동일한 네이밍을 가진 함수인자 값으로만

기능이 구분되는 것은 좋지 않은 방식입니다

 

그리고 use 함수의 에러 미들웨어 기능

동일한 use 함수에서 매개변수 인자 값의 개수 4개면

에러 미들웨어의 기능을 구현하게 된다는 것입니다

 

이 경우 인자 값을 누락하게 된다면 에러가 발생하거나 

전혀 생각지도 않은 기능이 구현된다는 것입니다 

 

애초에 use 함수가 아닌 확실하게 구분할 수 있는 네이밍이었다면

더 좋을 것 같다같다는 생각이었습니다

 

 


 

 

express 서버를 공부하면서

모듈을 조합해서 만든 라이브러리라는 것을 느꼈습니다

하지만 이런 것도 공부가 되리라 생각합니다

 

여러 언어를 배우면서 생각의 전환을 하거나

약간 내가 생각지도 못한 것들을 생각하게 한다는 것이지요

그러면서 저의 실력도 늘어나는 것이 아닐까 합니다

 

그럼 어려운 공부 조금 더 힘내셔서

우리의 IT 대모험을 헤쳐나가시길 기대합니다

 

그럼 다음 포스팅도 기대해주세요!

 

 

 

728x90
반응형
728x90
반응형

이전 포스팅 express 패키지 활용하여 서버 구축 및 html 서빙에 대해서

 

express에서 html 서빙 방법은 정말 간단합니다

express 내에서 html관련 패키지를 사용하고 있어

개발자들은 단지 메서드를 호출하는 것만으로 html 서빙을 할 수 있습니다

 

조금 더 자세한 내용을 알고 싶으시다면 아래의 링크를 클릭하시길 바랍니다

 

2022.09.14 - [IT_Web/Nodejs] - Node활용해서 express 서버로 HTML 노출 및 서빙하기

 

Node활용해서 express 서버로 HTML 노출 및 서빙하기

이전 글 Npm이 무엇이며 활용법에 대해서 node에서 npm을 빼고는 말을 할 수 없을 정도로 중요한 부분을 차지합니다 그 이유는 많은 사람들이 개발한 오픈 소스를 관리해 주기 때문이지요 npm을 잘

tantangerine.tistory.com

 

 

지금 글은 이전 포스팅과 이어지는 부분이 있으니

이전 포스팅이  궁금하시거나 필요한 부분이 있으면 위의 링크를 클릭하시길 바랍니다

 


 

express 서버 미들웨어 적용하기

 

일단 미들웨어란 무엇일까요??

 

사전적 의미로는 양 쪽을 연결하여 데이터를 주고받을 수 있도록

중간 매개 역할을 하는 소프트웨어라고 할 수 있습니다

 

 express 서버의 미들웨어는 중간 매개 역할이 핵심이라고 할 수 있습니다

아래의 app.get() 메서드를 라우팅이라고 합니다

 

라우팅을 할 경우 에러가 발생할 경우

특정 api url이 호출될 경우

존재하지 않은 api url 호출될 경우

 

특정 함수를 실행,

에러 페이지를 노출,

특정 페이지를 노출 등

 

여러 가지의 공통 작업을 할 수 있습니다

 

 

express 서버 미들웨어 적용하기

 

아래의 코드를 보시면 app.use()가 정의되어있습니다

이 함수는 라우팅을 호출할 경우 app.use()가 실행합니다

 

즉, 라우팅 get()은 함수의 인자 값이

클라이언트에서 보낸 api 호출 url이 동일한 라우팅이 실행됩니다

 

이때 라우팅이 호출되면 라우팅 get()보다 

우선 use()인 미들웨어가 호출을 감지하여 우선 실행합니다

그리고 next()를 실행하게 되면서 다음 미들웨어나 라우팅이 실행됩니다

 

이때 미들웨어의 위치도 중요합니다

 

기본적으로 실행 순서는 위에서부터 아래까지 실행합니다

그래서 라우팅이 호출될 때마다 미들웨어인 use()최상단부터 하단까지 실행된다고 생각하시면 됩니다

 

하지만 라우팅보다 미들웨어가 아래에 있다면 라우팅이 실행이 끝나는 순간

더 이상 실행하지 않고 종료가 됩니다

 

아래의 코드를 보면서 더 설명을 이어가겠습니다

 

 

 

const express = require('express');
const path = require('path');

const app = express();
app.set('port', process.env.PORT || 3000);

app.use((req, res, next) => {
  console.log('Hello, Express ::: 1');
  next()
},(req, res, next) => {
  console.log('Hello, Express ::: 2');
  next()
},(req, res, next) => {
  console.log('Hello, Express ::: 3');
  next()
})

app.get('/', (req, res) => {
  res.sendFile(path.join(__dirname, '/index.html'));
});

app.use((req, res) => { 
  res.send('페이지 호출 오류')
});

app.listen(app.get('port'), () => {
  console.log(app.get('port'), '번 포트에서 대기 중');
});

 

 

위의 코드를 작성하고 npm start를 입력하고

인터넷 브라우저 주소창에 "http://localhost:3000/"를 입력하게 되면

 

아래와 같이 커맨드 창이 노출됩니다

 

express_미들웨어_적용
express 라우팅 적용 결과

 

오른쪽에 보시면 최상단에 위치한 미들웨어부터 실행되며

그다음 라우팅이 호출되어 

주소창에 http://localhost:3000/"/"이 부분이 라우팅에 감지되어 위와 같은 결과가 노출됩니다

하지만 여기서 "/top"를 입력하면 아래와 같이 노출됩니다

 

 

express_미들웨어_적용2
express 미들웨어 적용 결과

 

top라는 라우팅 호출 주소가 동일하지 않아

최하단에 위치한 미들웨어가 실행되어 페이지 호출 오류 문구가 노출됩니다

 

이 결과 라우팅이 실행되지 않으면 미들웨어가 반응한다는 것을 알 수 있습니다

 

그리고 미들웨어 또한 호출 url주소를 감지할 수 있습니다

아래와 같이 코드를 변경해보도록 하겠습니다

 

express_미들웨어_적용3
express 미들웨어 호출 url 주소제어

 

위와 같이 코드를 변경하고 실행한 다음

브라우저 주소창 "http://localhost:3000/"를 입력합니다

 

위의 결과와는 다르게 커맨드 창에 최상단 미들웨어가 실행되지 않은 현상을 확인할 수 있습니다

 

express_미들웨어_적용6
express 미들웨어 url 주소 함수 결과

 

브라우저 주소창에 "http://localhost:3000/top"를 입력하면

아래와 같이 커맨드 창이 노출되는 것을 확인할 수 있습니다

 

express_미들웨어_적용4
express 미들웨어 url 주소제어 결과

 


 

 

 

미들웨어 에러 미들웨어 적용하기

 

이번에는 라우팅 내에서 Error가 발생할 경우 

제어하는 에러 미들웨어를 만들어 보겠습니다

 

 

const express = require('express');
const path = require('path');

const app = express();
app.set('port', process.env.PORT || 3000);

app.get('/', (req, res) => {
  try {
    csole.log('Hello, Express ::: 1');
    res.sendFile(path.join(__dirname, '/index.html'));
  } catch (e) {
    console.log('Error, Express ::: 00', e)
    throw new Error(e)
  }
});

app.listen(app.get('port'), () => {
  console.log(app.get('port'), '번 포트에서 대기 중');
});

 

 

위의 코드를 실행하고 브라우저도 이전과 같은 주소를 입력합니다

그럼 아래와 같은 화면을 노출합니다

 

console.log()를 철자가 틀리게 일부러 코드 작성하였습니다

그래서 에러가 발생하였습니다

 

express에서는 기본적으로 에러가 발생하면 아래와 같이 화면을 처리합니다

하지만 아래와 같은 에러 페이지서버의 디렉터리 구조가 상세히 명세되어있습니다

그렇기 때문에 보안상 취약할 수밖에 없기 때문에 직접 처리해야 합니다

 

 

express_에러_미들웨어_적용
express 라우터 에러 발생 페이지

 

에러 미들웨어는 아래와 같이 코드를  최하단에 추가합니다

*app.listen() 위에 추가하셔야 합니다

 

코드 추가할 경우 error와 next매개변수의 위치를 꼭 지켜주셔야

에러 미들웨어를 구현할 수 있습니다

 

 

express_에러_미들웨어_적용2
에러 미들웨어 코드 적용

 

아래와 같이 에러 페이지를 노출됩니다

 

 

express_에러_미들웨어_결과
에러 미들웨어 결과 페이지

 

 


 

미들웨어 http 상태 코드 제어하기

 

아래와 같이 코드를 추가합니다

미들웨어는 next를 추가하는 것을 꼭 확인하세요 

 

 

express_미들웨어_http_상태코드제어_적용
미들웨어 http 상태 코드 제어 코드

 

위와 같이 status(404)라고 호출한다면

아래와 같이 404로 http 상태 코드가 변한 것을 확인할 수 있습니다

미들웨어를 실행하면 에러가 아니기 때문에

위의 코드를 적용하지 않는다면 200번 코드가 표시됩니다

 

express_미들웨어_http_상태코드제어
미들웨어 http 상태 코드 적용 결과

 

 

 

이것을 활용한다면 클라이언트에 모든 http 상태 코드일괄적으로 설정해서 보낼 수 있습니다

 

그렇게 일괄적으로 보내게 되면 개발하는데 어려움이 있을 수 있습니다

 

다만, 운영 단계의 서비스 경우에는

모든 에러가 404로 보내게 되면

해커들에게는 지금 어떤 에러가 발생하는지 알 수 없기 때문입니다

 

보안으로는 좋은 면이 있습니다

 

 


 

 

node에서 express는 아주 많이 사용되고 있으며,

서브 웹 서버로도 많이 사용하고 있습니다

 

SI 개발자라면 node보다는 자바를 많이 사용하며,

최근 react로 개발하는 프로젝트가 많아지면서 

node의 중요성도 조금씩 높아지고

자연스레 express의 서브 웹 서버로써 활용도가

조금씩  높아지고 있습니다

 

SI 개발자라면 어떤 언어라도 공부를 해야겠지요

그러니 흥미가 가는 언어는 아무거나 공부를 하셔도 됩니다!

 

도움 안 되는 공부는 없으니까요

 

그렇게 공부하다 보면 코드를 간결하게 되며 함수적 프로그래밍도

자연스레 몸으로 습득할 수 있을 것입니다

 

그럼 우리의 IT 대모험을 위해서

파이팅 하시길 바랍니다

 

그럼 다음 포스팅에서 뵙겠습니다

728x90
반응형
728x90
반응형

이전 글 Npm이 무엇이며 활용법에 대해서

node에서 npm을 빼고는 말을 할 수 없을 정도로 중요한 부분을 차지합니다

그 이유는 많은 사람들이 개발한 오픈 소스를 관리해 주기 때문이지요

 

npm을 잘 활용한다고 하면 패키지를 어떻게 잘 관리할지에 대해서

조금은 생각을 하고 있는 사람이 아닐까 합니다

 

더 자세한 사항을 알고 싶으시다면 아래의 링크를 클릭하시길 바랍니다 

 

 

2022.09.10 - [IT_Web/Nodejs] - Node에서 중요한 NPM은 무엇이며, 그 활용법을 알아보자

 

Node에서 중요한 NPM은 무엇이며, 그 활용법을 알아보자

이전 포스팅 puppeteer 활용해서 웹크롤링에 대해서 웹크롤링은 업무적으로도 필요한 경우가 있습니다 그리고 배우는 것만으로도 충분히 재미를 느낄 수 있다고 생각합니다 그리고 웹크롤링은 비

tantangerine.tistory.com

 


 

Nodejs 활용해서 express 서버 구축하기

 

우선 처음 프로젝트를 만들 때 무엇을 해야 할지 모를 경우가 많습니다

일단 처음 node를 설치하였다면 프로젝트를 진행할 폴더에서

npm init을 하여 간단한 물음에 답을 합니다

 

그렇게 하면 package.json 생성되고

필요한 패키지를 설치하여 프로젝트를 진행합니다

 

그럼 파일이 생성되어 아래와 같은 구조를 가지게 될 것입니다

(index.html은 제가 미리 만든 파일입니다)

 

express_파일구조
파일 구조

 

그럼 express를 간단히 구현해 보도록 하겠습니다

 

먼저 개발에 유용한 패키지 nodemon을 설치하여 진행하겠습니다 

 

npm i -D nodemon

 

nodemon은 파일을 저장할 때마다 다시 npm start를 하지 않고

소스코드를 반영할 수 있게 하는 패키지입니다

 

아래와 같이 scripts를 설정하시고 진행하시길 바랍니다

 

express_package_파일정보
package.json 파일 정보

 


 

express 패키지 사용하기

 

커맨드 창에서 아래와 같이 패키지를 설치합니다

 

npm i express

 

아래와 같이 express패키지를 불러옵니다

require()node에서 모듈을 불러올 경우 사용할 수 있습니다

 

프로젝트 내에서 모듈을 불러올 수도 있으며,

npm이 관리하는 패키지일 경우에는

패키지명을 인자 값으로 할당해서 패키지를 불러올 수 있습니다

 

아래와 같이 서버를 만들고 

app.set()를 만들어서 port번호를 설정하게 됩니다

 

*env는 추후에 다시 설명해드리겠습니다

 

const express = require('express');
const path = require('path');

const app = express();
app.set('port', process.env.PORT || 3000);

app.get('/', (req, res) => {
  console.log('req: ', req)
  console.log('res: ', res)
  res.send(`Wellcome, Express Server`);
});

app.listen(app.get('port'), () => {
  console.log(app.get('port'), '번 포트에서 대기 중');
});

 


 

express로 html 서빙하기

 

 

그럼 인터넷 브라우저를 열어 주소창에 "http://localhost:3000/" 입력합니다

 

아래와 같이 화면이 노출될 것입니다

왜 이렇게 노출될까요?

 

express_html_노출
express 노출 화면

 

아래의 app.get()는 라우터라는 명칭으로 서버에서 선언하여 사용합니다

이때 api 호출 방식은 get방식, post방식이 있습니다

 

*get방식 post방식의 차이는 추 후에 다시 설명해 드리겠습니다

 

그 라우터는 첫 번째 인자 값에 api 호출받을 주소를 지정하여

get방식을 호출하는 api 주소가 동일할 경우  두 번째 인자 값인 콜백 함수가 실행됩니다 

 

그 콜백 함수에서 (req, res) 두 개의 매개변수가 보일 것입니다

 

이 두 개의 매개변수의 정보들을 보면 화면이 왜 그렇게 노출되는지에 대한

궁금증을 풀 수 있을 것 같습니다 

 

아래의 코드에 제가 console.log를 호출해 놓았습니다

그럼 콘솔 창에 어떤 정보가 있는지 확인해 봅시다

 

 

express_get_변경
express 콘솔 찍어보기

 

 

아래는 req에 대한 정보가 json Object형으로  담겨있습니다

req정보는 클라이언트에서 요청할 때 보낸 정보들이 담겨있는 것을 알 수 있습니다

아래에 statusCode, url, params, query, route 등등 유용한 정보들이 있습니다

 

express_req_정보
express req 정보

 

 

그럼 실제 클라이언트에서는 어떻게 보내고 있는지 한번 확인해볼까요?

F12키를 눌러서 개발자 모드에서 api요청 보낸 정보들이 헤더에 담겨있는 것을 확인할 수 있습니다

 

 

express_클라이언트_요청
express 클라이언트 api요청 상세정보

 

그리고 두 번째 매개변수 res의 정보를 보겠습니다

res는 express에서 자체적으로 사용하는 패키지와 모듈이 있습니다

 

 

당연 req에서 받은 정보들도 res에서도 사용할 수 있도록 저장되어있으며

html을 기본 코드들이 내장되어있어

res.send()에서 매개변수 인자 값으로 문자열을 할당할 경우

그 인자 값의 정보들이 내장된 html 코드와 같이 페이지를 노출하게 됩니다

 

결과 값은 아래와 같습니다

 

express_html_자체서빙하기
express 자체 내장 html 서빙 화면

 

이 것을 이용해서 html 파일을 미리 만들어서 사용할 수 있습니다

위에 제가 미리 만들어 놓은 html은 확인하셨을 것입니다

 

 index.html을 아래와 같이 코드 작업을 합니다

 

 

<html>
<head>
  <meta charset="UTF-8" />
  <title>Express Server</title>
</head>
<body>
  <h1>Express Server</h1>
  <p>환영합니다.</p>
</body>
</html>

 

그리고 아래처럼 res.sendFile()를 활용해서 경로를 적용해서 매서드를 호출합니다

path node의 패키지로 프로젝트의 경로를 찾아 할당하기 유용한 패키지입니다

 

app.get('/', (req, res) => {
  res.sendFile(path.join(__dirname, '/index.html')); // 변경코드
});

 

코드를 변경했다면 아래와 같이 변경된 화면을 확인할 수 있습니다

 

 

express_html서빙하기
HTML 파일 읽기로 HTML 서빙결과

 

 


 

 

express 서버 구현하는 것을 알아보았습니다

아직 미들웨어, DB 연동, 세분화된 라우터, 세션, 쿠키 활용 등

아직 갈길이 멀기만 합니다

 

블로그에 포스팅을 업로드할 때마다

콘텐츠를 걱정하는 블로거들이 많다고 합니다

하지만 저는 그런 걱정은 별로 안 하는 것 같습니다

그저 제가 공부한 것들이나 알고 있는 것들을 풀기만 하면 되니깐요

 

그래서 요즘은 블로그에 글을 쓰려고 공부하는지 공부하려고 글을 쓰는지

알 수 없을 정도가 되었습니다

 

하지만 그것 또한 저 한태는 좋은 영향을 주는 것 같습니다

동기부여와 더 진취적으로 변해가는 저의 모습을 보니깐요

 

1일 1 포스팅이 왜 어려운지 요즘 많이 생각하게 됩니다

 

앞으로도 꾸준할 수 있도록 노력하겠습니다

그러니 다 같이 파이팅 하시길 바랍니다

 

우리의 IT 대모험은 아직 끝나지 않았으니까요!

 

그럼 다음 글에서 뵙겠습니다

728x90
반응형
728x90
반응형

이전 글에서 데이터 모델링이 철학적 관점이 필요한 이유에 대해서

 

비즈니스 업무를 데이터적 관점으로 보는것은 매우 어려운일이다

그것을 서양 철학적 관점으로 해석으로 데이터 관점을 새롭게 바라볼 수 있는게 하였다

 

아래에 상세한 내용이 있으니 관심이 있으신분을 링크를 클릭하 시길 바랍니다

 

 

 

2022.09.12 - [Data_Modeling/Methodology] - 데이터 모델링을 철학적 관점이 필요한 이유는?

 

데이터 모델링을 철학적 관점이 필요한 이유는?

이전 글에서 데이터 모델링 정규화에 대해서 정규화 과정이 왜 중요하고 그것이 원론적으로 무엇인지 알아보았습니다 정규화가 정말로 성능적으로 문제가 있을까에 대해서도 알아보았는데 정

tantangerine.tistory.com

 


 

엔터티 모델링이 어려운 이유는?

 

엔터티 모델링 어려운 이유를 한마디로 표현하면 엔터티의 핵심적인 특성을 결장하는 기준을 찾기 어렵기 때문입니다

이러한 어려운 이유를 세분화하여 나열해 보았습니다

 

 

첫째 데이터 집합을 정의하기가 쉽지않습니다

 

모델링은 결국 업무 데이터의 분류와 묶음이라는 행위입니다

 

여신 연체관리 진행이라는 은행 업무가 있다고 한다면

<여신연체관리진행>이라는 엔터티를 만들어야 하는 지,

아니면 <여신연체관리> 엔터티의 <진행> 속성으로 관리해야하는지는

그 기준을 정하기가 어렵다는 것입니다

 

그 기준이 무엇인지 명확히 밝히고 객관화할 수 있다면 실전에 활용하면 될 것입니다

 

 

둘째 데이터의 본질을 볼 줄 알아야 엔터티를 정확하게 정의할 수 있는 점입니다

 

비즈니스 관점을 제거하는 게 그리 쉬운일은 아닙니다

더욱 데이터 모델링하는 사람들은 업무상 처음 마주한 사람이 대부분입니다

그렇기 때문에 본연의 속성을 파악하기가 쉽지 않을 것입니다

우리는 대상을 인식할 때 우선 복합물로 인식하기 때문입니다

 

존재에 고유한 특성인지 파악하고 정리할 수 있는 깊은 통찰력이 모델러에게 필요합니다

 

 

셋째 엔터티의 추상화 수준을 결정하는 것은 대단히 어렵습니다.

 

추상화 수준이란 다양한 의미를 포함합니다

사람이라는 집합이라면 이를 성별이라는 관점으로 구분할지, 태어는 나라로 구분할지

그것도 아니면 한 덩어리로 관리할지 전략적으로 결정하기가 쉽지가 않습니다

 

필요에 따라 유사 집합과 통합하고 분리하는 과정을 거치면서

엔터티의 적절한 크기를 결정할 수 있는 내공이 필요합니다

 

 

넷째 하위의 트랜잭션 데이터만을 보고

부모 역할을 하는 상위의 논리적인 집합을 발견하는 것은 어렵습니다.

 

 

데이터 모델링에서 엔터티 모델링은 꽃입니다

엔터티를 찾아내지 못하면 모델은 모호하거나 이상하게 그려지고 

결국 데이터가 중복되거나 이상현상이 발생합니다.

 

눈에는 보이지는 않지만, 분명히 존재하는 논리적인 집합을 발견할 수 있는 통찰이 필요합니다.

 

 

다섯째 업무의 방대함과 복잡도에 압도되기 쉽습니다.

 

차세대 정보시스템 구축 프로젝트의 경우

새롭게 만들어지는 테이블 수가 만 개 이상인 경우도 있습니다

 

모델러가 데이터의 정체성, 성격, 특징을 파악하고

데이터가 생성되는 규칙까지 읽을 수 있어야 제대로 된 모델이 나올 수 있습니다

 

하지만 실무에서는 부족한 분석/설계 기간,

작업 분류 체계 자체의 문제점, 업무 정형화에 필요한 현업 인터뷰 부족 등

많은 문제점들이 있습니다

 

이러한 환경 속에서 주어진 리소스만 가지고 업무의 복잡도를 파악하고

효율을 극대화해주는 방법론이 필요합니다

 

그렇기 때문에 더 더욱 경험이라는 것이 중요하며

자기만의 업무분야에서 사용할 방법론을 구축해야합니다  

 

 

프로젝트 성패를 결정짓는 데이터 모델링 이야기 中 김상래 지음

 

 


 

데이터 모델링을 공부하기위해 여러 기관도 알아보고 책도 많이 보았습니다

이론적인 부분만 본다면 저는 이 책이 정말 좋았습니다

 

데이터 모델링에 대해 근본적인 생각을 고쳐주며

다른 관점을 보게해주기 때문입니다

 

하지만 이런 관점을 실무에서 쓸수 있냐 없냐는 자기가 직접 구현해보면서

공부를 해야한다느 것이지요

 

그래서 이런 관점을 생각하면서 현재 프로젝트 모델링이 되어있는 것을 보면서

생각해보는 것이 필요하겠습니다

 

 

728x90
반응형
728x90
반응형

이전 글에서 데이터 모델링 정규화에 대해서

정규화 과정이 왜 중요하고

그것이 원론적으로 무엇인지 알아보았습니다

 

정규화가 정말로 성능적으로 문제가 있을까에 대해서도

알아보았는데 정규화는 성능적인 문제보다는 본질적인 개체를 구분해야

성능면에서도 뛰어나다는 것을 알 수 있었습니다

 

조금 더 상세한 정보를 알고 싶다면 아래 링크를 클릭하시길 바랍니다

 

 

2022.08.23 - [Data_Modeling/Methodology] - 데이터 모델링 정규화 란? 무엇이며, 정규화 성능 저하 정말 그럴까?

 

데이터 모델링 정규화 란? 무엇이며, 정규화 성능 저하 정말 그럴까?

이전 포스팅에서 정규화 이론이 왜 중요한가? 데이터 이상 현상을 제거하기 위해 정규화 이론은 중요하다고 하였습니다 데이터 구조적인 측면에서 중복되는 데이터에 대해서 독립 개체로 관리

tantangerine.tistory.com

 

 

데이터 모델링에 철학적 관점이 필요한 이유는?

 

 

먼저 우리는 데이터 모델링에 대해서 몇 가지 관점을 알고 있을 것입니다

 

1. 데이터는 업무 프로세스와 무관하며, 프로세스에 종속적이지 않아야 한다.

 

2. 비즈니스적 업무 및 절차가 변경된다고 데이터 모델링이 변경되어서는 안 된다.

 

3. 데이터 모델은 업무의 요건을 함축적으로 정의가 되어야 한다.

 

이렇게 3가지 정도를 알 수 있습니다

하지만 위의 사항이 약간은 이해가 안되는 부분이 있습니다

 

데이터 모델업무를 표현해야 한다면서,

동시에 업무와 종속성은 최소화되어야 한다는 것 때문입니다

 

이 부분은  데이터 모델은 업무를 데이터 관점으로

명확히 표현해야 한다는 것만은 알아야 하겠습니다

 

조금 더 설명하자면

 

이벤트라는 업무 활동을 하는데 이벤트라는 여러 가지 현상이 일어날 수 있지만

데이터 모델링은 변경이 되어서는 안 된다는 것입니다

 

이벤트라는 원론적인 본질에서 나타나는 여러가지 현상을

하나의 데이터 모델링으로 만들 수 있게 해야 한다는 말이겠지요

 

어느 프로젝트를 보면 이벤트를 만들 때마다 테이블을 늘려가며

페이지도 계속 추가되는 형태가 있습니다

하지만 그 현상을 일으키는 근본적인 원형은 이벤트라는 것을 알아야 할 것입니다

 

이벤트라는 근본적인 본질이 여러 가지의 이벤트라는 현상을 어떻게 범용적으로 품을 수 있을지 생각해야 합니다

 

이벤트 페이지의 크기 규격화하고, 버튼위치를 명세화하며, 버튼의 갯수를 정의할 수 있게 하는 등

기능들을 어드민 페이지에서 핸들링 할 수 있다면 

분명 이벤트 페이지도 모델링에 적합한 데이터들이 있을 것입니다 

 

이전 포스팅에 설명한 설문지에 대해서 모델링이 참고할만한 자료가 되겠지요

 

그래서 아리스토텔레스는 "현상은 복잡하지만 본질은 단순하다"

직접 보이는 세계를 현상이라 하며, 나무 같은 개별 존재의 공통 속성은 본질이라 합니다

추석 이벤트, 출석 이벤트를 현상이라고 본다면 이벤트 자체는 좀 더 원형적인 본질로 이해해야 한다는 것입니다

 

이처럼 수많은 현상과 사건은 하나하나가 매우 독특하고 개별적이며 끊임없이

변화하기 때문에 안개처럼 겹쳐 본질을 흐리기 쉽습니다

 

현상에 속지 않고 본질을 들여다보면서 대상을 명확히 하려는 태도와 마음가짐

모델링에서 무엇보다 중요합니다

 

과감하게 현상을 버리고 본질을 관통하려면 본질을 기반으로 세상을 이해하는 통찰적 시각을 가져야 합니다.

데이터 모델러는 경험이나 인식의 범위를 초월하여 비즈니스 세계의 본질과 현상을 철저히 분리할 수 있는

높은 수준의 통찰력이 요구됩니다

 

이를 근간으로 만들어지는 본질과 현상을 분리할 수 있도록 도움이 되는 인식론을 알아보고자 합니다

 

 

실제 중심적 철학

 

서양 철학의 사유 방식 중인 하나는 플라톤과 아리스토텔레스의 인식 방식인 실제 중심적 철학입니다

아리스톤 텔레스는 사유 체계인 범주론을 보면 실제, 분량, 성질, 관계, 장소 시간, 위치 상태 등으로

구성되는 범주 표가 있습니다

 

이처럼 사유 유형 중에서 본질을 뜻하는 실체로서의 존재는 다른 것과 관련하지 않고

오직 자신과만 관련 있다는 의미로 존재는 독립적이다라고 말하고 있습니다

 

관계 중심적 철학

 

위와 다른 사유 방식인 관계(사건, 현상)입니다

반 플라톤적 사상가인 질 들뢰즈에 의해 선명하게 나타납니다

 

현실세계의 모든 사건, 행위, 현상은 영토성 코드성으로 이루어집니다

 

야구공, 배트, 글러브, 야구선수, 심판, 관중 등이 일정하게 자리를 차지하는 영토성이며,

 

야구 규칙과 스포츠 관람이라는 일정한 코드가 작동함으로써 코드성 

야구 경기가 성립되어 배치성이라는 의미를 가집니다

 

 

즉,

야구 선수인 이승엽은 어느 야구시합에서 만루 홈런을 친 것은 배치성을 나타내며

야구장, 각종 야구용품, 선수 등이 영토성을 가지며

이미 존재하는 야구 규칙이 서로 만나 사건으로 인식되는 것입니다

 

이때 사건과는 무관한 순수하고 본질적인 영역영토성 존재가 모델링의 주요 관심 대상이 됩니다

 

 

프로젝트 성패를 결정짓는 데이터 모델링 이야기 中 김상래 지음 

 


 

위의 글처럼 우리는 데이터 모델링이라는 관점을 새로운 측면에서 봐야 합니다

지금 일어난 사건과 현상에만 집중하지 말고 본질을 볼 수 있는 눈을 키워야 하며

본질에서 어떠한 현상이 나타나는지 잘 판단해서 모델링을 해야 합니다

 

데이터 모델링은 아무리 공부해도 어려운 것 같습니다

다음에는 실제 사례들로 지금까지 공부한 것을 접목하여 글을 올리도록 하겠습니다

 

그럼 오늘도 즐거운 하루 되셨길 바라겠습니다

728x90
반응형
728x90
반응형

이전 포스팅 puppeteer 활용해서 웹크롤링에 대해서

웹크롤링은 업무적으로도 필요한 경우가 있습니다

그리고 배우는 것만으로도 충분히 재미를 느낄 수 있다고 생각합니다

그리고 웹크롤링은 비동기식으로 해결해야 해서 Promise를 자주 쓰며 async, await도 자주 활용합니다

그래서 이 개념을 잡는 공부도 가능하니 꼭 한번 공부해보세요

 

더 자세한 사항이 궁금하다면 아래의 링크를 클릭하시면 됩니다

 

 

2022.08.20 - [IT_Web/Nodejs] - nodejs 웹 크롤링을 Puppeteer 및 Promise 활용 완벽 파헤치고 CSV 파일 쓰기

 

nodejs 웹 크롤링을 Puppeteer 및 Promise 활용 완벽 파헤치고 CSV파일 쓰기

이전 포스팅에 대하여 이전 포스팅에서는 간단하게 웹 크롤링을 하는 방법을 알아보았습니다 axios와 cheerio를 사용해서 웹 크롤링을 해보았습니다 하지만 axios는 한계가 있습니다 싱글 페이지로

tantangerine.tistory.com

 

 

npm이란?

Node Package Manager의 약자로 여러 사람들이 만든 모듈을 모아둔 저장소입니다

npm에 저장된 모듈을 프로젝트 내에서 설치를 하여 간편하게 구현이 가능합니다

이러한 모듈들이 오픈 소스 생태계를 만들고 있습니다

이 모듈은 노드로 구성되어 있으며,

그것을 패키지라 명합니다.

 

 

npm 활용 패키지 설치하기

 

npm i [패키지] / npm i -g [패키지]  / npm i -D [패키지]

위 3가지 설치 방법이 있습니다

각기 다른 방법은 저장 위치가 다르며 그 사용법도 조금씩 다르게 구성됩니다

 

첫 번째 방법으로 설치하면 dependencies 저장되어 json 형태로 패키 지명이 입력됩니다

그래서 어떤 패키지가 설치되었는지 확인이 가능합니다

 

-D: 설치된 패키지는 개발에서만 사용될 수 있도록 devDependencies에 분리되어 관리가 됩니다

 

-g: 글로벌 설치를 하면 dependencies와 devDependencies에서 설치된 이력을 찾을 수 없고

사용할 경우에도 npm을 붙여서 사용하기보다는 패키지명만 커맨드 창에 입력하면 사용할 수 있습니다

하지만 패키지를 커맨드 창에서 입력해 사용하는 일은 많이 없습니다

그래서 설치 이력에도 안 남아서 관리인원이 변경되면 유지 보수하기에 많은 어려움이 있기 때문에

최근에는 많이 사용하지 않는 추세입니다 

 

package_json파일
package.json 파일

 

그리고 npm init을 하고 설치를 하면 아래와 같이 파일 구조가 형성됩니다

node_modules는 패키지가 설치되면 그 관련 모듈들이 설치되어

그 파일들의 패키지명이 나열되어있는 것을 확인할 수 있습니다

 

또한

package-lock.json이라는 파일을 확인할 수 있습니다

그 파일은 패키지의 버전들이 고정되어 관리하는 파일

 

package_lock_json파일
package-lock.json파일 형성 구조

 

 

npm에서 패키지 버전 관리 SemVer(유의적 버저닝) 방식에 대하여

 

Major(주 버전), Minor(부 버전), Patch(수 버전)의 형태로 버전이 관리된다

아래와 같이 버전이 관리되어있다면

 

express : "^4.18.1" 

 

Major 버전인 4에 해당하는 위치의 수는

어떠한 수정으로 하위 버전과 호환이 안될 경우 버전이 올라갑니다

 

Minor 버전인 18에 해당하는 위치의 수는

어떠한 수정으로 하위 버전과 호환이 되는 경우 버전이 올라갑니다

 

Patch 버전인 1에 해당하는 위치의 수는

기능에 버그를 해결했을 때 올라갑니다

 

 

 

버전 기호 사용법

 

이쯤 되면 "^" 이러한 기호가 궁금증을 유발합니다

 

^4.18.1: 패키지 업데이트 시 Minor 버전까지만 업데이트됩니다

즉, Major는 고정되고 그 이하 버전만 업데이트됩니다.

 

~4.18.1: 패키지 업데이트 시 Patch 버전까지만 업데이트됩니다.

즉, Minor이상은 고정되고 그 이하 버전만 업데이트됩니다.

 

그리고 <=, >=, <, >는 이상, 이하, 초과, 미만으로 사용할 수 있습니다

하지만 사용하지 않습니다

 

알파/베타/RC 버전이 존재할 수도 있습니다

(1.1.1-alpha.0, 2.0.0-beta.1, 2.0.0-rc.0)

 

설치할 때 버전을 지정해서 설치할 수 있습니다

npm i express@3.6.2

 

@latest는 최신을 의미합니다

그래서 아래와 같이 사용할 수 있습니다

npm i express@latest

 

 

npm 명령어 알아보기

 

npm outdated를 커맨드 창에 입력합니다

 

아래와 같이

현재 버전과 현재 상용 버전 그리고 최신 버전이 함께 노출됩니다

그럼 버전을 확인한 다음에

package.json에 패키지의 버전을 변경하여

npm update를 하게 되면 버전이 반영됩니다

 

npm_outdated
npm outdated 결과

 

버전 관리 방법은 이외에도 npm-check를 사용하면 조금 더 편하게 버전을 관리할 수 있습니다

npm i -g npm-check으로 패키지를 설치합니다

 

npm-check를 입력하면 

아래의 화면처럼 노출이 됩니다

MAJOR UP / NOTUSED? / NEW_VER! / MINOR UP / PKG ERR 등으로

패키지 버전 상태를 체크할 수 있습니다

 

 

npm_check
npm-check 결과 화면

 

그리고 버전을 간편하게 업데이트하는 방법도 있습니다

npm-check -u를 입력하게 되면 아래와 같은 화면이 노출되면서 버전을 관리할 수 있습니다

스페이스 바를 누르면 (*) 패키지가 선택되며 최종적으로 엔터키를 누르면

아래의 상용 버전으로 업데이트가 됩나다

 

npm_check_u
npm-check -u 입력 화면

 

 

 

아래의 버전을 업데이트하는 모습입니다

그리고 업데이트 결과를 보여주며 업데이트가 끝이 나게 됩니다

 

npm_check_installing
npm-check 설치중 화면

 

npm_check_업데이트결과화면
npm-check 업데이트 결과 화면

 

npm uninstall [패키 지명]: 패키지를 삭제하는 명령어입니다

 

npm search 검색어: npm 패키지에서 검색할 수 있는 사항들을 검색할 수 있습니다

 

npm_search_화면
npm search expo 입력 화면

 

npm info [패키 지명]으로 패키지의 정보를 확인할 수 있습니다

패키지를 만든 개발자 메일현재 상용 배포된 버전 상태

현재 패키지와 연관되어 있는 패키지 정보도 알 수 있습니다

키워드로 expo가 보입니다 npm search [keyword]로 검색 정보를 위와 같이 얻을 수 있습니다

 

npm_info_화면노출
npm info expo 입력시 화면노출

 

npm ls는 프로젝트 내에

어떤 패키지를 사용하고 있는지 트리구조로 볼 수 있습니다

 

아래와 같이 노출됩니다

 

npm_ls_화면노출
npm ls 화면 노출

 

이렇게 npm에 대해서 알아보았습니다

프로젝트를 진행하면서 npm에 대해서 제대로 모르고 사용한 것은 아닌가 하고 생각하게 되었습니다

이번 기회에 조금 더 깊이 알아보았으니 더 활용해보아야겠네요 

 

그럼 오늘 추석인데도 불구하고 블로그를 쓰는 저에게 칭찬을 주며

IT 대모험은 끝나지 않음을 확인하였습니다

 

그럼 모두들 즐거운 추석 되시고 

다음 글에서 뵙겠습니다

 

 

728x90
반응형
728x90
반응형

이전 글에서 엑셀 데이터를 읽어와 DB에 저장하기

 

때로는 현업과 아님 타 부서에서 엑셀 데이터를 어드민 페이지에서 볼 수 있게 해달라고 한다면,

그리고 엑셀 파일에 수백 가지의 데이터가 있다면,

어떻게 하겠습니까? 

수 작업으로 데이터에 저장하기는 어려운 일입니다

 

그래서 엑셀을 업로드해서 자바에서 읽을 수 있는 방법을 모색해보았습니다

 

자세한 사항이 알고 싶으시면 아래의 링크를 클릭하시면 됩니다

 

 

2022.08.05 - [IT_Web/Java] - Java 엑셀 데이터 읽어오기 및 다중 insert 한방에 하기

 

Java 엑셀 데이터 읽어오기 및 다중 insert 한방에 하기

오늘은 엑셀 데이터를 업로드해서 자바에서 데이터를 받아서 insert를 하든 아니면 다시 그 데이터를 다시 조합해서 웹에 다시 전달하는 등 핸들링하는 방법을 알아보겠습니다 그리고 클래스명

tantangerine.tistory.com

 

메일 보내기 앞서 Velocity란? 무엇인가?

메일을 보내기 위해서는 HTML 태그가 필요하며

head태그와 body에 각종 태그를 삽입하여 템플릿을 만들어서 

일정한 코드를 삽입해서 로고나 이미지를 보여줄 수 있습니다

하지만 태그만 필요한 것이 아닌 DB에서 관리하는 데이터들도

같이 범용성 있게 노출시켜야 합니다

그러기 위해서 vm파일에 데이터를 같이 합성하여 사용하기 위해

velocity를 사용합니다

 

 

velocity 설정 및 추가하기

Velocity를 활용하기 위해 pom.xml에 추가합니다

 

<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity</artifactId>
    <version>1.7</version>
    <scope>system</scope>
    <systemPath>${webapp.lib}/velocity-1.7.jar</systemPath>
</dependency>

 

 

bean과 필요한 값을 설정합니다

로그인 및 비밀번호와 패스워드를 설정합니다

그리고 메일을 보낼 서비스단도 추가합니다

 

 

<!-- smtp 메일전송 -->
<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"> 
    <property name="host" value="mail.######.net"/> 
    <property name="port" value="25" /> 
    <property name="username" value="xxxxxx@naver.net"/> 
    <property name="password" value="#####"/> 
    <property name="javaMailProperties"> 
        <props> 
          <prop key="mail.smtp.auth">true</prop>
          <prop key="mail.debug">true</prop>
          <prop key="mail.smtp.auth.mechanisms">LOGIN</prop>
        </props> 
    </property> 
</bean>

<bean id="mailer" class="kr.co.xxxxxx.common.mail.Mailer">
    <property name="velocityEngine" ref="velocityEngine" /><!--velocityEngine bean참조 -->
</bean>

<bean id="velocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean">
    <property name="resourceLoaderPath" value="WEB-INF/mailtemplate/"/>
</bean>

 

 

위의 코드에 "WEB-INF/mailtemplate/"를 확인할 수 있습니다

그 저장소에는 mailtemplate.vm이 지정되어있습니다

아래와 같이 설정되어 메일 형식을 지정하여 보낼 수 있습니다

 

<!DOCTYPE html>
<html lang="ko">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title></title>
</head>
<body>
  <!-- header -->
  <table width="600" border="0" align="center" cellpadding="0" cellspacing="0" style="text-align:left;font-size:16px;font-family:Malgun Gothic, '맑은고딕', Dotum, '돋움', Tahoma, Helvetica Neue, Helvetica, sans-serif;background-color:#fff;margin:0 auto">
    <tbody>
      <tr><td height="100">&nbsp;</td></tr>
      <tr>
        <td>
          <table width="600" border="0" align="center" cellpadding="0" cellspacing="0">
          <tbody>
          <tr>
            <td width="139" align="left"><a href="#"><img src="images/logo.png" alt="autobell" border="0"/></a></td>
            <td width="133">&nbsp;</td>
          </tr>
          </tbody>
          </table>
        </td>
      </tr>
    </tbody>
  </table>
  <!-- //header -->

  <!-- content -->
  <table width="600" border="0" align="center" cellpadding="0" cellspacing="0" style="text-align:left;font-size:16px;font-family:Malgun Gothic, '맑은고딕', Dotum, '돋움', Tahoma, Helvetica Neue, Helvetica, sans-serif;background-color:#fff;margin:0 auto">
    <tbody>
      <tr><td height="70">&nbsp;</td></tr>  
      <tr>
        <td>
          <table width="600" border="0" align="left" cellpadding="0" cellspacing="0" style="text-align:left;font-size:16px;font-family:Malgun Gothic, '맑은고딕', Dotum, '돋움', Tahoma, Helvetica Neue, Helvetica, sans-serif;background-color:#fff">
            <tbody>
            <!-- title -->
            <!-- //title -->

            <!-- text type1 -->
            <tr><td height="50">&nbsp;</td></tr>
            <tr>
              <td style="color:#222;font-size:18px;line-height:1.6">
                '${NAME}'님
            </td>
            </tr>
            <!-- //text type1 -->

            <!-- text type1 -->
            <tr><td height="50">&nbsp;</td></tr>
            <tr>
              <td style="color:#222;font-size:18px;line-height:1.6">
                번호 : '${NO}'<br />
                성명 : '${NAME}' <br />
                도착예정시간 : '${TIME}'<br />
                연락처: '${PHONE}'
              </td>
            </tr>
            <!-- //text type1 -->

            <!-- design line -->
            <tr><td height="56">&nbsp;</td></tr>
            <tr>
              <td>
                <table width="600" border="0" align="left" cellpadding="0" cellspacing="0">
                  <tbody>
                    <tr>
                      <td height="1" width="72" style="border-top:1px solid #d8d8d8">&nbsp;</td>
                      <td height="1">&nbsp;</td>
                    </tr>
                  </tbody>
                </table>
              </td>
            </tr>
            <!-- //design line -->

            <!-- text type2 -->
            <tr><td height="40">&nbsp;</td></tr>
            <tr>
              <td style="color:#222;font-size:14px;line-height:1.6">
            </td>
            </tr>
            <!-- //text type2 -->

            <!-- link -->
            <tr><td height="25">&nbsp;</td></tr>
            <tr>
              <td>
              </td>
            </tr>
            <!-- //link -->
            </tbody>
          </table>
        </td>
      </tr>
      <tr><td height="56">&nbsp;</td></tr>
    </tbody>
  </table>
  <!-- //content -->

  <!-- footer -->
  <table width="600" border="0" align="center" cellpadding="0" cellspacing="0" style="text-align:left;font-size:16px;font-family:Malgun Gothic, '맑은고딕', Dotum, '돋움', Tahoma, Helvetica Neue, Helvetica, sans-serif;background-color:#fff;margin:0 auto">
    <tbody>
    <tr>
      <td>
        <table width="600" border="0" align="center" cellpadding="0" cellspacing="0" style="text-align:left;font-size:16px;font-family:Malgun Gothic, '맑은고딕', Dotum, '돋움', Tahoma, Helvetica Neue, Helvetica, sans-serif;background-color:#fff;border-top:1px solid #aaa">
          <tbody>
            <tr><td height="30">&nbsp;</td></tr>
            <tr>
              <td style="font-size:14px;color:#777;line-height:1.5"><br />
            </tr>
            <tr><td height="27">&nbsp;</td></tr> 
            <tr>
              <td style="font-size:12px;color:#777;line-height:1.5">본 메일은 발신전용 메일로 회신이 되지 않습니다.<br />
            </tr>
          </tbody>
        </table>
      </td>
    </tr>
    <tr><td height="100">&nbsp;</td></tr>
    </tbody>
  </table>
  <!-- //footer -->
  
</body>
</html>

 

 

그럼 Velocity 설정 및 추가는 끝이 났습니다

이제는 데이터를 저장할 VO를 보며 어떤 데이터를 받을지 지정해봅시다

 

VO가 정의되어있습니다

템플릿을 미리 만들어놓고 적용한다면 true를 할당하여 메일링 값에 적용합니다

그리고 수신자, 발신자, 제목, 내용도 함께 할당되어 

서비스단의 메서드를 호출할 것입니다

 

 

 

public class MailVO {
    private boolean isTemplate = true;  // 템플릿적용 여부
    private String from = "";           // 발신자
    private String to = "";             // 수신자
    private String bcc = "";            // 숨은 참조자
    private String subject = "";        // 제목
    private String contents = "";       // 내용
    private String templateName = "";   // 메일템플릿명
    private String logoImageUrl = "";   // 로고이미지 경로
    private String successYn = "";
    private Map<String, Object> contentsMap; // Mapping Contents
    
    
    ....
}

 

 

아래와 같이 서비스단을 호출하게 됩니다 

로고, 제목, 내용을 정의하여 mailer.sendMail() 호출합니다

 

 

@Override
public void sendMailTemplate(MailVO mailVO) throws Exception {
    try {
        mailVO.setTemplateName(StringUtils.isBlank(mailVO.getTemplateName()) 
                                ? "commonTemplate.vm" 
                                : mailVO.getTemplateName());

        Map<String, Object> contentsMap = new HashMap<String, Object>();
        contentsMap.put("isTemplate", mailVO.isTemplate());
        contentsMap.put("subject", mailVO.getSubject());
        contentsMap.put("logoImageUrl", StringUtils.isBlank(mailVO.getLogoImageUrl()) 
                                        ? "/images/common/h1-logo.svg" 
                                        : mailVO.getLogoImageUrl());
        contentsMap.put("contents", mailVO.getContents());
        if(mailVO.getContentsMap() != null) {
            contentsMap.putAll(mailVO.getContentsMap());
        }
        
        mailVO.setContentsMap(contentsMap);
        mailer.sendMail(mailVO);
    } catch (Exception e) {
        logger.error("[ R E S ]/{} {}", "", StackTraceUtil.getStackTraceString(e));
    }
}

 

 

그리고 mailSender 객체로 MimeMessage의 객체를 만들어서 merge 작업하여

msg.setText()에 값을 정의하여

mailsender.send()로 메일을 발송합니다

 

그렇게 되면 메일 발송은 완료됩니다

 

 

@Autowired
private VelocityEngine velocityEngine;

@Autowired
private JavaMailSender mailSender;

@Autowired
private CommonLogService commonLogService;

public void setVelocityEngine(VelocityEngine velocityEngine) {
    this.velocityEngine = velocityEngine;
}

public void sendMail(MailVO mailVO) {
    MimeMessage msg = mailSender.createMimeMessage();
    mailVO.setSuccessYn("N");
    try {

        String veloTemplate = VelocityEngineUtils.mergeTemplateIntoString(velocityEngine, mailVO.getTemplateName(), "UTF-8", mailVO.getContentsMap());
        msg.setText(veloTemplate, "UTF-8", "html");
        if(!mailVO.getBcc().equals("")){
            msg.setRecipients(Message.RecipientType.BCC, mailVO.getBcc());
        }

        mailSender.send(msg);
        mailVO.setSuccessYn("Y");
    } catch (ParseErrorException | MethodInvocationException  e) {
        logger.error("[ R E S ]/{}{}", StackTraceUtil.getStackTraceString(e));
        return;
    } catch (ResourceNotFoundException e) {
        logger.error("[ R E S ]/{}{}", StackTraceUtil.getStackTraceString(e));
        return;
    } catch (MessagingException e) {
        logger.error("[ R E S ]/{}{}", StackTraceUtil.getStackTraceString(e));
        return;
    } catch (MailException e) {
        logger.error("[ R E S ]/{}{}", StackTraceUtil.getStackTraceString(e));
        return;
    } catch (Exception e) {
        logger.error("[ R E S ]/{}{}", StackTraceUtil.getStackTraceString(e));
        return;
    }

    try {
        commonLogService.insertMailLog(mailVO);
    } catch(Exception ex) {
        ex.printStackTrace();
    }

}

 

 

 

 

오늘은 자바를 활용해서 메일을 발송하는 방법을 알아보았습니다

node와 Java의 방식은 정말 다르기에

두 개 중 무엇이 좋다고 할 수 없지만

 

프로젝트 환경과 규모에 따라 다르게 대응하겠지요

그러니 많은 언어를 알고 있다는 것은 큰 장점일 수 있습니다

하지만 구글링만으로도 구현할 수 있다면

괜찮습니다 우선 빨리 구현하고 나서

난중에 다시 복습하면서 왜 이렇게 되는지 알아보는 게 핵심입니다

 

프로젝트 진행할 때는 결과를 빨리 보여주고

개별적으로 다시 코드 리뷰를 하면서 리팩터링과 모듈화 등을 하면서

자기 실력을 쌓아가는 것이 좋을 것 같습니다

 

그럼 다음 글도 기대해주시고

우리들의 IT 대모험은 끝나지 않았으니 힘내시길 바랍니다

 

 

 

 

728x90
반응형
728x90
반응형

이전 글에서는 웹크롤링에서 간단하게 DB 연동하기에 대해서

 

DB 연동으로 웹크롤링을 보다 효율적으로 사용하는 방법을 알아보았습니다

DB 연동으로 정보를 저장해서 데이터 관리는 물론 중복된 데이터까지

관리가 가능하니 웹 크롤링을 보다 능률적으로 실행할 수 있을 것입니다

 

더 자세히 알고 싶으시다면 아래의 링크를 클릭해주세요

 

 

2022.09.03 - [IT_Web/Nodejs] - 웹 크롤링 puppeteer로 mysql 및 sequelize 활용하여 DB 연동하기

 

웹 크롤링 puppeteer로 mysql 및 sequelize 활용하여 DB연동하기

이전 글에서 간략하게 프록시가 무엇이며 프록시를 활용해서 IP를 변경하는 방법을 알아보았습니다 더 자세한 정보를 알고 싶으시다면 아래의 링크를 클릭하시길 바랍니다. 2022.09.02 - [IT_Web/Nodej

tantangerine.tistory.com

 

 

로그인 유지 및 파일 업로드 알아보기

 

 

오늘은 puppeteer를 활용해서 파일 업로드

로그인 정보를 유지하는 방법을 알아보려고 합니다

 

 

 

그럼 우선 전체 코드를 보시겠습니다

아래의 코드를 보시면 진행이 많이 되었습니다

이전 포스팅을 하나씩 참고하시면 완성하실 수 있으니 관심 있으시다면 확인해보세요

 

 

 

const puppeteer = require('puppeteer');
const writekeyboardList = require('./writekeyboardList');
const callElement = require('./mouseHandleHtml');
const dotenv = require('dotenv'); // npm i dotenv
const db = require('./models');
const fs = require('fs')

dotenv.config();

const setCrawler = async (production, isMouse = false) => {
  await db.sequelize.sync();
  let browser = await puppeteer.launch({
    headless: false,
    args:['--window-size=1920,1080'],
    userDataDir: 'C:\Users\kimsangjin\AppData\Local\Google\Chrome\User Data'
  });
  let page = await browser.newPage();
  await page.setViewport({
    width: 1080,
    height:1080,
  })

  if (!production && isMouse) {
    await callElement(page)
  }

  await page.on('dialog', async (dialog) => { 
    console.log(`dialog`, dialog) 
    console.log(`type: ${dialog.type()} / message: ${dialog.message()}`) 
    if(dialog.type() === 'prompt') {
      await dialog.accept('https://www.tistory.com/')
    }
    if(dialog.type() === 'alert'){
      await dialog.dismiss(); 
    }
    if(dialog.type() === 'confirm'){
      await dialog.accept();
    }
  })

  const setNewProxy = async(crawlerUrl) => {
    const newProxies = await page.evaluate(async() => {
      const ips = Array.from(document.querySelectorAll('tr > td:first-of-type > .spy14')).map((v) => v.innerText)
      const types = Array.from(document.querySelectorAll('tr > td:nth-of-type(2)')).map((v) => v.innerText)
      const anonymity = Array.from(document.querySelectorAll('tr > td:nth-of-type(3)')).map((v) => v.innerText)
      const latencies = Array.from(document.querySelectorAll('tr > td:nth-of-type(6) > .spy1')).map((v) => v.innerText)
      return ips.map((v, i) => {
        return {
          ip: v,
          type: types[i],
          latency: latencies[i],
          anonymity: anonymity[i].toString()
        }
      });
    });
    const fristProxies =  newProxies.filter((v) => v.type.startsWith('HTTP')).sort((p, c) => p.latency - c.latency)
    console.log('fristProxies', fristProxies)
    await Promise.all(fristProxies.map(async (v) => {
      return await db.Proxy.create({
        ip: v.ip,
        type: v.type,
        latency: v.latency,
        anonymity: anonymity.toString()
      });
    }))
    await page.close();
    await browser.close();
    browser = await puppeteer.launch({
      headless: false,
      args:['--window-size=1920,1080', '--disable-notifications', `--proxy-server=${fristProxies[0].ip}`]
    });
    page = await browser.newPage();
    await page.goto(crawlerUrl);
    await page.waitForSelector('.tf_keyword')
    await inputValue('.tf_keyword', '내아이피'),
    await Promise.all([
      buttonClick(['.ico_pctop.btn_search', 'ico_ksearch.btn_ksearch']),
      page.waitForNavigation(),
    ]);
    await setTime(4000)
    await page.close();
    await browser.close();
    await db.sequelize.close()
  }

  const handleWriteKeyboard = async (selector, id) => {
    const writeList = writekeyboardList()
    const idOfList = [...id]
    if(selector){
      await page.click(selector);
      console.log(`idOfList`, idOfList)
      for (const id of idOfList) {
        const inputValue = await writeList.filter(element => element.ShiftLeft === id || element.key === id )
        if (inputValue.length > 0) {
          const code = inputValue[0].code
          const ShiftLeft = inputValue[0].ShiftLeft === id ? 'ShiftLeft' : ''
          if (ShiftLeft) {
            await setTime(selector, 0, 'key')
            await page.keyboard.down(ShiftLeft);
            await setTime(selector, 0, 'key')
            await page.keyboard.press(code);
            await setTime(selector,0, 'key')
            await page.keyboard.up(ShiftLeft);
          } else {
            await page.keyboard.press(code);
          }
          await setTime(selector,0, 'key')
        }
      }
    } else {
      await page.keyboard.press(id);
    }
  }

  const getSelectEl = async(selector) => {
    const selectedEl = await page.evaluate((selector) => {
      console.log('selector', selector)
      const temEl = document.querySelector(selector)
      console.log('selector', temEl)
      return temEl
    }, selector)
        console.log('selector', selectedEl)
    return selectedEl
  }

  const fileUpload = async() => {
    const pathFile = 'C:\Users\kimsangjin\Desktop'
    const fileExitst = await fs.existsSync(`${pathFile}/travis_1.png`)
    if(fileExitst){
      const input = await page.$("#m_attach_file");
      await input.uploadFile(`${pathFile}/freelanceDeveloper.png`)
      return true
    }
    return false
  }

  const inputValue = async (selector, value) => {
    await page.evaluate((selector, value) => {
      document.querySelector(selector).value = value
    }, selector, value)
    await setTime(selector)
  }

  const buttonClick = async (selector, nextSelector = '', type = '') => {
    await page.evaluate((selector) => {
      if (document.querySelector(selector[0])) {
        document.querySelector(selector[0]).click();
      } else {
        document.querySelector(selector[1]).click();
      }
    }, selector)
    if(type === 'login'){
      // waitForRequest(요청 대기), waitForResponse(응답 대기)
      const res = await page.waitForResponse(res => {
        return res.url().includes('graphql')
      })
      const result = await res.json()
      console.log('login', result);
      if(await result.extensions.is_final){
        await setTime(type, 2000)
        await page.keyboard.press('Escape')
      }
    } else if(type === 'logOut'){
      await page.waitForNavigation()
    } else {
      if(nextSelector){
        await page.waitForSelector(nextSelector)
      }
      if(type === 'wait') await setTime(selector)
    }
  }

  const setTime = async (selector = 'normal', waitTime = 0, type= '') => {
    const setTime = await page.evaluate(async(waitTime, type) => {
      let timeList =[]
      if(type === 'key'){
         timeList = await [569, 311, 257, 175, 306, 123, 428, 497, 298, 211, 379, 108, 512, 139, 249];
      } else {
        timeList = await [3975, 4547, 5231, 6397, 7894, 3394, 4206, 5147, 6932, 7430, 3561, 4896, 5877, 6407, 7133];
      }
      let randomTime = await timeList[Math.floor(Math.random() * timeList.length)]
      if (waitTime > 0) randomTime = waitTime
      await new Promise(r => setTimeout(r, randomTime))
      return randomTime
    }, waitTime, type)
    console.log(`waitTime[${selector}]: `, setTime)
    return setTime
  } 


  return { fileUpload, getSelectEl, buttonClick, inputValue, handleWriteKeyboard, setTime, setNewProxy, page, browser }
}


const crawler = async () => {
  try {
    const { getSelectEl, buttonClick, inputValue, handleWriteKeyboard, setTime, setNewProxy, page, browser } = await setCrawler(JSON.parse(process.env.PRODUCTION));
    const id = await process.env.EMAIL;
    const password = await process.env.PASSWORD;
    await page.goto('https://office.hiworks.com/polarium.co.kr/home#loginUrl=mail/webmail/m_list/b0');
    await setTime()
    const isLogin = await getSelectEl('.menu.office')
    if(isLogin){
      console.log('이미 로그인이 되어있습니다.')
      await buttonClick(['.menu.office'], '.main-btn');
    } else {
      await page.waitForSelector('#office_id');
      await setTime()
      await handleWriteKeyboard('#office_id', id)
      await setTime()
      await handleWriteKeyboard('#office_passwd', password)
      await setTime()
      await buttonClick(['.int_jogin'], '.main-btn');
    }
      await page.waitForSelector('.main-btn')
      await buttonClick(['.main-btn'], '#to_addr');
      await page.waitForSelector('#to_addr')
      await inputValue('#to_addr', '김상진');
      await setTime('', 500)
      await page.keyboard.press('Enter');
      await page.waitForSelector('#m_attach_file')
      await fileUpload()
      await setTime('', 500)
      await buttonClick(['.detail_select > a']);
  } catch (e) {
    console.log(e);
  }
}

crawler()

 

로그인 유지 알아보기

먼저 로그인 유지 방법을 알아보도록 하겠습니다

 

 

아래의 userDateDir를 추가하면 간단하게 로그인 유지가 됩니다

하지만 크롬만 가능하니 크롬을 설치하고 아래의 경로대로 설정하시면 간단히 됩니다

 

puppeteer-로그인유지
로그인 유지 코드

 

아래처럼 로그인 유지가 된다면

로그인에만 있는 태그를 선택해서 있으면 로그인이 되었다는 의미로

분기를 하여 코드 작성할 수 있습니다

 

 

 

puppeteer-로그인분기
로그인 코드 분기

 

 

 

 

puppeteer 활용하여 파일 업로드 하기

 

이제는 어느 정도 하실 수 있다고 가정하겠습니다

그래서 여러 함수를 세팅해서 사용하고 있으니 참고하시길 바랍니다

 

로그인 후

fileUpload함수를 아래와 같이 실행합니다

 

 

puppeteer-파일업로드함수호출
파일업로드 실행

 

 

우선 파일 위치가 필요합니다

그 위치에 현재 올리려는 파일이 있는지 존재 여부를 확인하여

있을 때만 파일 업로드를 실행합니다

 

이때 이름은 db에 저장된 파일을 올리도록 하는 것이 좋을 듯합니다

다음에 기회 되면 적용하도록 하고

지금은 특정 파일만 올리도록 하겠습니다

 

 

puppeteer-파일업로드선언문
파일업로드 선언문

 

 

 

 

아래의 이미지를 보면 파일 첨부라는 글이 보이지만

그 밑에 input type='file'로 정의된 것을 확인할 수 있습니다

그것을 위에 처럼 input을 가져와서 .uploadFile()와 경로를 지정해서 호출하면

 

파일이 업로드되는 것을 확인할 수 있습니다

이때 꼭 page.$()활용해서 해야 한다는 점 확인하세요

 

 

 

태그 현재 상황
태그 현재 상황입니다

 

 

 

이렇게 로그인 유지와 파일 업로드를 알아보았습니다

node.js에서 메일 보낼 수 있는 라이브러리가 존재합니다

그러니 그것도 활용하는 것이 좋을 듯합니다

크롤링보다는 라이브러리를 사용하는 것이 간편하겠지요

 

그럼 오늘도 한걸음 나아가셨길 바라며

IT대모험을 즐기며 다음 포스팅에서 찾아뵙겠습니다

그럼 파이팅 하세요!

728x90
반응형
728x90
반응형

* SVN 설치 및 사용방법

1. 아마존에 있는 visual SVN repository에 폴더를 만들고 아이디 비밀번호 등록하고 폴더에 접근할 권한 설정

 

2. 이클립스나 STShelp> eclips marketplace 들어가서 svn으로 검색 subclipse 설치

    Window> show view에서에서 SVNrepositories

    SVNrepositories에서 우클릭 New > RepositoryLocation


3. 창이 뜨면 URL에 서버 저장소입력

권한 확인을 위해서 아이디 비밀번호 입력하는 창이 노출됩니다

 

1번에서 만든 아이디 비밀번호를 입력해하면 레파지토리에 폴더가 생성됩니다

(에러가 발생하면 권한 설정에 문제이니 VisualSVN에서 아이디를 만들거나 권한 설정을 확인해주세요.)

 

4. 프로젝트를 svn에 올려보겠습니다.

올릴 프로젝트를 우클릭한 후 Team > share project

Commit을 누르고 창이 뜨면 comment는 수정내용을 기입하면 됩니다! Ex) project setting,

(성공하면 올린 사람(마지막 수정자) 아이디가 파일 옆에 뜨고, SVN repositories에도

파일이 올라온 것을 확인할 수 있습니다.)

 

5. 이제 올린 파일을 내려받아 보겠습니다

SVN 레파지토리에서 받을 파일 우클릭 > checkout 하면 폴더가 통째로 local

받아집니다. 올린 파일이 안 보일 경우 레파지토리 우클릭 > refresh 하면 보일 거예요

 

6. 새로운 파일을 만들고 수정을 한 후 commit을 해볼게요

Local 프로젝트 안에 새로운 폴더를 만들고 local 프로젝트 > 우클릭 > team > synchronize with repository

SVN 안에 있는 파일이랑 local에 있는 파일과의 싱크를 볼수 있습니다


하나의 폴더와 파일이 생성됐어요.
4번에서 했던 것처럼 우클릭해서commit 해주시면 됩니다!

 

7. 수정한 프로젝트를 update 해보겠습니다

local 프로젝트 > 우클릭 > team > synchronize with repository

우클릭 > update 하면 수정된 부분이 local에 적용이 됩니다!

 

8. 이제 누가 뭘 수정했는지 보겠습니다 Team > show history

한 두 개 뜰 테고 하나하나 눌러보면 언제 누가 어떤 부분 수정했는지 까지 다 볼 수 있어요

 

9. Ingnore을 설정해보겠습니다.
Ignore 기능은 무시하고 올리는 파일들을 설정하는 건데,

예를 들면 설정 파일을 계속 올리고 내리고 할 필요도 없고,

수정을 막 하면 안 되니 설정 파일 같은 것들을 ignore 설정해놓으면

그 파일들만 무시하고 받을 수 있습니다

Ex) pom.xml

Window > preference > team 검색 > ignore resource > add pattern

 

<<SVN사용 시>>

전체 커밋은 하지않는게 좋습니다

다른 사람이 수정 및 추가한 파일 날아가고SVN에 자기 local에 있는 프로젝트 그대로 올라가게 되니 주의하시길바랍니다

처음 시작할때 꼭 update를 하고 commit을 하길바랍니다

에러가 날 경우 대부분 team > refresh/clean up 하거나

본인이 수정한 부분 메모장에 옮겨둔 후 team > update to Head

team > syncronise with repositories > override update 한 후 다시 수정하고 commit,

team > markresolve, edit conflict 충돌 해결해주기

충돌이 나면 우선, 메모장 두 개 켜서 본인 local 소스랑 svn 소스 옮겨놓고 시작하세요 날아갈지도 모릅니다!!

* GitHub설치 https://shxrecord.tistory.com/115?category=677810

* GitHub사용법 https://shxrecord.tistory.com/116

 

* 서버에 접속할 수 있는 고정 IP로 변경하여 접속해라고 할 수 있다 https://extrememanual.net/12161

- 이 웹사이트 접속하여 변경 방법을 기억하자

 

728x90
반응형

'IT_Web > Project_Setting' 카테고리의 다른 글

GitHub 사용법  (0) 2020.03.09
GitHub 설치방법  (0) 2020.03.09
자바 설치 및 환경 설정 스키마 계정생성  (0) 2020.03.09
728x90
반응형

 

이전 글에서 SI 프리랜서 개발자의 인력난에 대해서 

 

최근 개발자 관심이 높아지면서 프리랜서의 정규직 전환이 많아졌습니다

그래서 프리랜서가 필요한 프로젝트에서는 인력난으로 단가도 높아지고

지금 프로젝트 투입된 개발자를 계속 붙잡으려고 에이전시도 많은 노력을 하고 있는 추세였습니다

 

더 자세히 알고 싶으시면 아래의 링크를 클릭해주세요

 

2022.07.24 - [IT_Developer/About_Developers] - 프리랜서 IT 개발자 구인난 왜 그렇게 극심한 걸까? 그 이유는?

 

프리랜서 IT 개발자 구인난 왜그렇게 극심한 걸까? 그 이유는?

프리랜서 개발자 시장 인력이 부족한 이유는? 코로나 시대에 접어들고 비대면 서비스가 늘어나면서 IT 업종에 대한 관심도가 높아졌다 여러 기업들이 IT에 대한 역량을 키우기 위해 애썼고 그 결

tantangerine.tistory.com

 

 

SI란 무엇일까?

 

요구되는 서브 시스템들을 잘 아울러서 큰 시스템을 구축/구현하는 일련의 프로세스를 말합니다

 

 

SI 개발자 단가를 알아보자

 

 

이 프로세스를 구축하기 위해서는 대규모의 프로젝트 인원이 필요하게 되고

그 인원을 정규직 인원으로 모두 채용하기에는 대기업, 공공기관에서는 다소 무리한 채용규모라

프리랜서를 고용해서 프로젝트를 진행하게 됩니다

 

그렇게 자연스레 공공기관 예산에서 프리랜서의 단가를 측정하면서

그 기준이 현재까지 물가 상승률에 비례해 조금씩 변동해 왔습니다

 

하지만 거의 상승하지 않았다고 봐야 하겠지요

최근 몇 년 사이에 조금 올라가기 시작했습니다

 

그렇게 아래와 같이 임금 표가 매년 발행됩니다

 

 

2022년-프리랜서-개발자-단가
2022년 프리랜서 임금 단가 표

 

 

 SI 개발자는

응용 SW 개발자에 해당한다

특급, 고급, 중급, 초급으로 구분되어있습니다

 

아무리 찾아보아도 2022년 평균 임금은 찾을 수가 없었습니다

 

아래와 같이 평균임금이 있지만

이 임금은 지금 현재 임금과는 다소 차이가 있습니다

 

 

 

SI개발자-단가
SI 개발자 단가

 

 

아래의 프리랜서 채용 공고만 보더라도

9년 이상 ~ 무관 780 ~ 800만원

7년 이상 ~ 무관 680 ~ 700만원 등등

프로젝트에 맞게 단가 형성이 되어있습니다

 

 

SI프로젝트-공고-개발단가
SI 프로젝트 공고 단가

 

 

 

아래에 더 자세히 보면

중급과 고급 1명을 채용하면서 단가는 아래와 같이 형성됩니다

 

 

 

 

프로젝트-프린랜서-단가
공고 상세문

 

 

지금 현재 단가는 SM단가라서 SI는 조금 더 높은 단가로 책정되고 있습니다

 

그리고 이렇게 형성된 단가가 모든 프로젝트에 적용되는 것은 아닙니다

개발 언어와 프로젝트의 규모에 따라 단가는 다르게 형성됩니다

 

 

끝으로

 

금액적인 문제라서 정말 민감한 이야기입니다만,

많은 사람들이 궁금해하는 글들을 보았습니다

그래서 한 번쯤은 이야기하는 것이 좋지 않을까 하는 생각에 글을 적게 되었습니다

 

SI 개발자 신입들이 에이전시에 뻥튀기로 팔려가는 것을 많이 보았고 체험을 했었지요

팔려가는 것이라고 표현한 것도 악덕 에이전시가 저지르는 많은 악행을 보고 생각했습니다

 

하지만 지금은 많이 개선되어서 그렇게 까지 하지 않는 것 같습니다

대기업들이 그 피해를 더 이상 용인하지 않기 때문이겠지요

 

그러면서 초급보다는 중급 이상 개발자를 선호하기는 합니다

위의 채용공고를 보더라도 초급을 채용하는 곳은 찾아보기가 어렵습니다

 

그래서 저는 프리랜서 전향하려는 개발자를 보면

적어도 4~5년은 정규직으로 일하면서 정규직에서만 배울 수 있는 것을 쌓아가라고 말을 합니다

사수가 있을때 모델링, 인프라, 쿼리 등등 배울 것이 많으니까요

 

너무 조급하게 생각하지 말고 4~5년의 중요한 시기를 알차게 배웠으면 합니다

 

프리랜서가 되면 혼자서 공부해야 하며 

더 이상은 사수가 없는 상황에서 혼자서 일을 해야합니다

당연히 중급, 고급분들이 도와준다면 물어보면서 할 수 있지요

 

프리랜서로서 혼자서 당당해지려면 어느 곳에 가서도

자기 힘으로 할 수 있다는 마음가짐이 중요한 것 같습니다

 

그리고 그게 왜 중요하냐면

고급, 중급이라고 해서 모든 사람이 다 잘한다고 할 수 없습니다

빌런을 만나면 그 사람 분까지 다 해야 하며, 못 한다고 정당하게 말해서 나올 수는 있지만

어떤 상황에서든 잘 해결해서 나오면 인정해주는 사람을 만나면서

인맥은 더 넓어질 것이며 그 소식은 에이전시도 알게 됩니다

그러면 PM님도 다음에 저를 불러줄 수도 있고

에이전시에서도 저와 같이하려고 신경써서 관리해주며 다른 곳을 가더라도

다음에는 자기들과 하자며 티타임을 하기도 합니다

 

프리랜서는 일적으로 스트레스도 많고 합니다

하지만 인정받고 일한다는 보람과 프로젝트를 마무리하고 전부는 아니지만

내가 참여한 프로젝트가 서비스하는 모습을 보면 자좀감도 올라가고

보람도 있습니다 그렇게 당당하게 생각할 수 있으려면 나 자신에게도 당당해야겠지요

 

이렇게 예전에는 힘든점도 있으며

지금은 많이 좋아지고 있는 것같습니다

 

지금에서야 개발자에 대해 인식이 좋아졌지만 5년 10년 전에는 

개발자 인식이 많이 좋지 않았습니다

선호하는 직종도 아니었고요

 

지금은 카카오, 네이버 등등 대기업과 벤처기업의 성공으로

it 기업에 대한 회사 인식도 좋아지면서 

선호하는 직업으로 각종 교육프로그램이 나오기 시작했지요

 

정부에서까지 IT에 대해 인식도 개선되면서

초등학교 정규과목으로 선정되는 등 많은 변화가 있었습니다

 

앞으로도 많은 변화가 있을 것이며

WEB 3.0이 현실화 되는 시점에서는 

개발자 인력은 더 많이 필요하게 될것이라 예상됩니다

 

그러니 우리자리에서 충실히 하다보면

또 다른 기회를 잡을 수 있지않을까 생각합니다

 

그럼 아직 끝나지않은 우리의 IT대모험을 즐겨보시길 바랍니다

그럼 파이팅하세요

 

 

 

 

 

 

 

 

 

 

728x90
반응형

+ Recent posts

Powered by Tistory, Designed by wallel