TortoiseSVN 을 사용할 경우.

Tortoise SVN을 사용할 경우 
 
command로 실행하는 방법은 크게 2가지 이다.
 
1.svn.exe를 이용하는 방법
2.TortoiseProc.exe를 이용하는 방법
 
 

1. SVN Command

 

svnadmin dump

   - 저장소 백업

   - svnadmin dump [저장소 폴더명] > [덤프 파일명]

   - ex) svnadmin dump project_sample > dump_project_sanmple_20010131

 

svnadmin load

   - 저장소 복원

   - svnadmin load [저장소 폴더명] < [덤프 파일명]

   - ex) svnadmin create project_sample

           svnadmin load project_sample < dump_project_sanmple_20010131

 

svn checkout

   - svn 서버에서 소스 내려받기

   - svn checkout svn://192.168.2.2/App App

   - svn checkout -r 100 svn://192.168.2.2/App App   (revision 100번 소스 받기)

   - svn checkout -r HEAD svn://192.168.9.128/sample_repos sample_HEAD     (마지막 리비전 받기)

   - 자세한 것은 svn checkout --help 참고

 

svn export

    - 버전관리 정보 없는 깨끗한 사본을 받아옴
    - svn export --help
    - svn export -r 1 svn://192.168.0.12/sample_repos sample_pos
 

svn commit 파일명 or 폴더명 or ./

   - svn commit 했을 때 소스 파일에서 out of date 에러 날 때는 그 사이에 svn에 소스가 업데이트 됬다는 의미이다. 따라서, out of date 에러났을 때는 svn update을 실행해서 동기화를 한 후에 commit을 하면 된다.

   - commit 하기전에는 update를 실행하여 out of date 에러를 막자.

   - 예)

      # svn commit

      # svn commit ./test/test.h

 

svn resolved -R ./
   - 소스 충돌 날 때 이놈으로 풀면 됨.


svn cleanup
   - 소스가 lock 되 있을 경우 이 명령으로 해제 한다. lock 되는 원인은 알아서 찾는다.

 

svn update

   - 서버와 소스 동기화.

      A  Added

      D  Deleted

      U  Updated

      C  Conflict

      G  Merged

      E  Existed

   - commit 하기전에는 소스 충돌을 막기 위해 update를 실행해야 한다.

   - update를 했는데 에러가 났을 경우에는 각각의 revision을 따로 update 해본다. 경험상 한번에 할 경우에 드물게 에러가 나는 경우가 있다.

      #svn update -r 100   --> 100은 revision number

      #svn update -r 101

 

svn log

   - svn user가 commit한 로그 볼 때 사용한다.

      # svn log svn://192.168.9.128/sample_repos     ==> 주의) svn path를 안주면 마지막 로그를 빼먹는다.

        # svn log -l 10   (상위 10개의 로그만 보기)       ==> 주의) svn path를 안주면 마지막 로그를 빼먹는다.

      # svn log -l 10 svn://192.168.9.128/sample_repos

    

   - commit log 수정하기

      # svn propset --revprop -r [리비전번호] svn:log “수정할 메세지”

       참고: http://altistory.net/929

      # TortoiseSVN 사용할 때 show log 선택 후에 로그 패널에서 마우스 오른쪽 버튼 클릭하면 "Edit log message" 나온다.

 

svn mkdir

   repository에 folder 생성한다.

    - # svn mkdir svn://192.168.163.11/TEST_repo/test_folder

 

svn import

   - 처음으로 repository에 파일 추가하기. 

    - 주의할점은 *.a 같은 파일은 추가 안될 수 있으니 --no-ignore 옵션 사용하기.

    - # svn import import_path svn://192.168.163.11/TEST_repo/import_path --no-ignore

 

svn add

   - 새로 만든 파일 추가 하기. import와 똑같이 사용 가능하다.

   - # svn add --no-ignore [path]

 

svn delete (or del, remove)

   - 파일, 폴더 삭제

   - # svn del [FILE_NAME] or [DIR]

   - # svn del svn://192.168.16.11/TEST_repo/test_dir

      --> commit도 동시에 된다. help 참고.

 

svn switch(or sw)

   - 저장소 이름 변경할 때

    # mv sample_repo1 sample_repo2 (su 로그인 후 저장소 이름을 직접 변경)

    # svn switch --relocate svn:/192.168.9.128/sample_repo1 svn:/192.168.9.128/sample_repo2

       --> repository에서 다운받은 곳에서 명령을 실행한다.

 

    "svn: E155007: 작업 사본이 아닙니다.(Not a working copy)" 이 에러는 .svn 없는 곳에서 svn 명령을 실행

     했을 때 발생한다.

    

   - 저장소 IP 변경할 때

    # svn switch --relocate svn:/192.168.30.198/sample_repo1 svn:/192.168.30.122/sample_repo1

 

svn diff

  - 두 revision 비교 또는 file 비교

  - 참고: http://pokute.tistory.com/201

변경된 파일만 출력

svn diff | awk '/^Index/ {print $NF}'

 

두 리비전 비교

# svn diff -r 100:101

 

두 리비전의 파일비교

#svn diff -r 100:101 file_path

 

참고 링크 : http://blog.daum.net/rayolla/251

 

2.거북이 SVN을 이용해서 실행하기

 

ex) TortoiseProc.exe /command:update /path:"경로" /closeonend:0

 

참고링크 : http://tortoisesvn.net/docs/release/TortoiseSVN_en/tsvn-automation.html

1.1 데이터 사이언스란?

1.1.1 데이터 사이언스의 정의

데이터 사이언스(Data Science)는 데이터를 수집, 처리, 분석, 모델링하여 가치 있는 인사이트(통찰력)를 도출하고 의사 결정을 지원하는 학문이자 기술 분야이다.

데이터 사이언스는 단순한 데이터 분석을 넘어 빅데이터, 인공지능(AI), 머신러닝(ML) 등과 결합하여 더욱 정교한 의사 결정을 가능하게 한다. 현대 사회에서는 방대한 양의 데이터를 효과적으로 처리하고 활용하는 것이 중요해졌으며, 데이터 사이언스는 이를 최적화하는 데 핵심적인 역할을 한다.

데이터 사이언스를 구성하는 주요 요소

  • 통계학(Statistics): 데이터를 요약하고 패턴을 분석하여 의미를 도출
  • 데이터 분석(Data Analysis): 데이터를 정리, 시각화, 해석하여 유용한 정보 제공
  • 머신러닝(Machine Learning): 데이터를 학습하여 패턴을 찾고 예측 모델 구축
  • 데이터 엔지니어링(Data Engineering): 대규모 데이터를 저장, 관리, 처리하는 기술
  • 데이터 시각화(Data Visualization): 데이터를 직관적으로 표현하여 쉽게 이해하도록 지원
  • 도메인 지식(Domain Knowledge): 특정 산업(의료, 금융, 마케팅 등)에 대한 이해를 바탕으로 분석 수행

데이터 사이언스의 핵심 목표

  • 데이터에서 의미 있는 인사이트(통찰력)를 도출하여 문제 해결
  • 데이터 기반의 의사 결정을 자동화하여 효율성 증대
  • 미래를 예측하는 모델을 구축하여 비즈니스 및 연구 분야에 활용

데이터 사이언스는 기업의 성과 개선, 연구 혁신, 정책 결정 등 다양한 영역에서 중요한 역할을 하며, 데이터를 활용하는 모든 분야에서 필수적인 요소로 자리 잡고 있다.


1.1.2 데이터 분석과 머신러닝의 차이

데이터 분석(Data Analysis)과 머신러닝(Machine Learning)은 데이터 사이언스의 핵심 요소이지만, 목적과 방법론이 다르다.

데이터 분석 (Data Analysis)

  • 과거 및 현재 데이터를 분석하여 패턴과 인사이트(통찰력)를 도출
  • 주요 기법: 통계 분석, 탐색적 데이터 분석(EDA), 데이터 시각화, 가설 검정
  • 결과: 데이터에 대한 해석과 설명

머신러닝 (Machine Learning)

  • 데이터에서 자동으로 패턴을 학습하고 예측하는 모델을 구축
  • 주요 기법: 지도 학습(Regression, Classification), 비지도 학습(Clustering, Dimensionality Reduction)
  • 결과: 예측 모델, 자동화된 의사 결정 시스템

데이터 분석 vs 머신러닝 비교 표

비교 항목 데이터 분석 (Data Analysis) 머신러닝 (Machine Learning)
목적 데이터의 의미와 패턴을 찾음 패턴을 학습하여 미래를 예측
방법론 통계 분석, EDA, 데이터 시각화 지도학습, 비지도학습, 강화학습
출력 결과 데이터의 해석 및 설명 자동화된 예측 및 의사 결정
활용 사례 고객 구매 패턴 분석, A/B 테스트 추천 시스템, 이미지 인식, 자율주행

데이터 분석은 데이터를 탐색하고 해석하여 의미를 도출하는 과정이며, 머신러닝은 데이터를 학습하여 자동으로 패턴을 찾고 미래를 예측하는 방법론이다.

데이터 분석은 머신러닝을 포함할 수도 있지만, 머신러닝은 분석보다 예측 및 자동화에 초점을 맞춘다는 점에서 차이가 있다.


1.1.3 데이터 사이언스가 활용되는 분야

데이터 사이언스는 다양한 산업에서 비즈니스 가치 창출과 문제 해결을 위해 활용되며, 기업과 연구 기관뿐만 아니라 공공 분야에서도 중요한 역할을 한다.

금융(Finance)

  • 사기 탐지(Fraud Detection): 신용카드 결제 및 금융 거래 데이터를 분석하여 이상 패턴 감지
  • 투자 및 리스크 관리(Investment & Risk Management): 주식시장 데이터 분석 및 알고리즘 트레이딩
  • 고객 맞춤 금융 서비스: 개인 맞춤형 금융 상품 추천

의료(Healthcare)

  • 질병 예측 및 진단(Disease Prediction & Diagnosis): 의료 데이터를 분석하여 질병 발병 가능성 예측
  • 의료 영상 분석(Medical Image Analysis): 딥러닝을 활용한 CT, MRI, X-ray 진단 자동화
  • 병원 운영 최적화(Hospital Management Optimization): 환자 대기 시간 예측 및 병상 관리

마케팅 및 고객 분석(Marketing & Customer Analytics)

  • 추천 시스템(Recommendation System): 고객의 소비 패턴을 분석하여 맞춤형 제품 추천
  • 고객 세분화(Customer Segmentation): K-Means 클러스터링을 활용한 고객 그룹화
  • 광고 최적화(Ad Optimization): A/B 테스트 및 데이터 기반 광고 성과 분석

자율주행 및 스마트 모빌리티(Autonomous Vehicles & Smart Mobility)

  • 자율주행 자동차(Autonomous Vehicles): 딥러닝을 활용한 객체 탐지 및 실시간 주행 예측
  • 교통 흐름 예측(Traffic Flow Prediction): 실시간 도로 데이터 분석을 통한 교통 체증 예측
  • 공유 모빌리티 최적화(Shared Mobility Optimization): 차량 이용 패턴 분석을 통한 최적 배차 모델 구축

결론

데이터 사이언스는 금융, 의료, 마케팅, 자율주행 등 다양한 산업에서 활용되며, 데이터 기반의 혁신을 가능하게 하는 핵심 기술이다.

데이터를 효과적으로 분석하고 모델링하여 비즈니스 가치와 효율성을 극대화하는 역할을 하며, 점점 더 많은 기업과 연구 기관에서 필수적인 요소로 자리 잡고 있다.

좋아요2
 

출처: https://gangdonggil.tistory.com/1382 [개발_노트:티스토리]

11.2 시계열 모델링 (Time Series Modeling)

시계열 모델링은 시간에 따라 변하는 데이터를 분석하고 미래를 예측하는 기법이다.
전통적인 통계 모델인 ARIMA, SARIMA와 딥러닝을 활용한 LSTM 모델이 대표적인 방법이다.

이 장에서는 ARIMA, SARIMA 모델과 LSTM을 활용한 시계열 예측 기법을 다룬다.


11.2.1 ARIMA 모델 (AutoRegressive Integrated Moving Average)

1) ARIMA란?

ARIMA(자기회귀 누적 이동 평균)는 시계열 데이터를 기반으로 미래 값을 예측하는 대표적인 통계적 모델이다.
이 모델은 자기회귀(AR), 차분(I), 이동 평균(MA)의 조합으로 시계열 데이터를 분석한다.

ARIMA 모델 구성 요소

  • AR (AutoRegressive, 자기회귀): 과거 값(시간 t 이전 값들)을 사용하여 현재 값을 예측
  • I (Integrated, 차분): 데이터의 안정성을 확보하기 위해 차분 연산 수행
  • MA (Moving Average, 이동 평균): 과거의 예측 오차를 사용하여 현재 값을 예측

📌 ARIMA 모델 수식

Y_t = c + φ_1 * Y_{t-1} + φ_2 * Y_{t-2} + ... + θ_1 * e_{t-1} + θ_2 * e_{t-2} + ... + e_t
  • Y_t: 현재 값
  • φ: 자기회귀 계수
  • θ: 이동 평균 계수
  • e_t: 예측 오차

2) ARIMA 모델 적용 (Python 코드 예제)

📌 Python 코드 예제 (ARIMA 모델을 활용한 시계열 예측)

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from statsmodels.tsa.arima.model import ARIMA

# 가상의 시계열 데이터 생성
date_range = pd.date_range(start="2023-01-01", periods=100, freq="D")
np.random.seed(42)
data = np.cumsum(np.random.normal(loc=0, scale=2, size=100)) + 100
df = pd.DataFrame({"Date": date_range, "Value": data})
df.set_index("Date", inplace=True)

# ARIMA 모델 학습 (p=2, d=1, q=2 설정)
model = ARIMA(df["Value"], order=(2, 1, 2))
model_fit = model.fit()

# 예측 수행 (향후 10일)
forecast = model_fit.forecast(steps=10)

# 결과 시각화
plt.figure(figsize=(10, 5))
plt.plot(df, label="Actual Data")
plt.plot(pd.date_range(start=df.index[-1], periods=11, freq="D")[1:], forecast, label="ARIMA Forecast", linestyle="dashed", color="red")
plt.xlabel("Date")
plt.ylabel("Value")
plt.title("ARIMA 모델 예측 결과")
plt.legend()
plt.show()

ARIMA 모델 활용

  • order=(p, d, q)에서
    • p: 자기회귀 차수
    • d: 차분 차수
    • q: 이동 평균 차수
  • model_fit.forecast(steps=n)을 사용해 미래 n일 예측 가능

11.2.2 SARIMA 모델 (Seasonal ARIMA, 계절성 ARIMA)

1) SARIMA란?

SARIMA는 ARIMA 모델에 계절성(Seasonality) 요소를 추가한 모델이다.
계절성을 포함하는 시계열 데이터(예: 월별 판매량, 계절별 온도 변화)에서는 ARIMA보다 SARIMA가 더 적합하다.

SARIMA 모델 구성 요소

  • ARIMA(p, d, q) 모델을 기반으로 계절성 요소 추가
  • 계절성 차수(P, D, Q, m)가 추가됨
    • P: 계절성 자기회귀 차수
    • D: 계절성 차분 차수
    • Q: 계절성 이동 평균 차수
    • m: 계절 주기 (예: 월별 데이터라면 m=12)

📌 SARIMA 모델 수식

SARIMA(p, d, q) × (P, D, Q, m)
  • 일반 ARIMA(p, d, q) 모델에 계절성 요소 (P, D, Q, m)를 포함

2) SARIMA 모델 적용 (Python 코드 예제)

📌 Python 코드 예제 (SARIMA 모델을 활용한 시계열 예측)

from statsmodels.tsa.statespace.sarimax import SARIMAX

# SARIMA 모델 학습 (p=1, d=1, q=1) × (P=1, D=1, Q=1, m=12)
sarima_model = SARIMAX(df["Value"], order=(1, 1, 1), seasonal_order=(1, 1, 1, 12))
sarima_fit = sarima_model.fit()

# 예측 수행 (향후 10일)
sarima_forecast = sarima_fit.forecast(steps=10)

# 결과 시각화
plt.figure(figsize=(10, 5))
plt.plot(df, label="Actual Data")
plt.plot(pd.date_range(start=df.index[-1], periods=11, freq="D")[1:], sarima_forecast, label="SARIMA Forecast", linestyle="dashed", color="red")
plt.xlabel("Date")
plt.ylabel("Value")
plt.title("SARIMA 모델 예측 결과")
plt.legend()
plt.show()

SARIMA 모델 활용

  • seasonal_order=(P, D, Q, m)를 설정하여 계절성 반영 가능
  • 주기적 변동이 있는 데이터에서 강력한 성능 발휘

11.2.3 LSTM을 활용한 시계열 예측

1) LSTM이란?

LSTM(Long Short-Term Memory)은 딥러닝 기반의 시계열 예측 모델로,
장기적인 패턴을 학습할 수 있어 ARIMA, SARIMA보다 더 복잡한 데이터 패턴을 예측하는 데 유리하다.

LSTM의 특징

  • 장기 의존성(Long-Term Dependency) 학습 가능
  • 비선형 관계를 학습할 수 있어 복잡한 시계열 데이터에 적합
  • 대량의 데이터에서 강력한 성능 발휘

2) LSTM을 활용한 시계열 예측 (Python 코드 예제)

📌 Python 코드 예제 (LSTM 기반 시계열 예측 모델 구현)

import tensorflow as tf
from tensorflow.keras import layers
from sklearn.preprocessing import MinMaxScaler

# 데이터 정규화 (LSTM은 데이터 정규화 필요)
scaler = MinMaxScaler()
df["Scaled_Value"] = scaler.fit_transform(df[["Value"]])

# 입력 데이터 생성 (과거 10일 데이터를 기반으로 예측)
sequence_length = 10
X, y = [], []
for i in range(len(df) - sequence_length):
    X.append(df["Scaled_Value"].values[i:i+sequence_length])
    y.append(df["Scaled_Value"].values[i+sequence_length])

X, y = np.array(X), np.array(y)

# LSTM 모델 생성
model = tf.keras.Sequential([
    layers.LSTM(50, return_sequences=True, input_shape=(sequence_length, 1)),
    layers.LSTM(50, return_sequences=False),
    layers.Dense(1)
])

# 모델 컴파일 및 학습
model.compile(optimizer="adam", loss="mse")
model.fit(X, y, epochs=20, batch_size=16)

# 예측 수행
y_pred = model.predict(X)

# 결과 시각화
plt.figure(figsize=(10, 5))
plt.plot(df.index[sequence_length:], y, label="Actual Data", alpha=0.5)
plt.plot(df.index[sequence_length:], y_pred, label="LSTM Prediction", linestyle="dashed", color="red")
plt.xlabel("Date")
plt.ylabel("Value")
plt.title("LSTM 모델 예측 결과")
plt.legend()
plt.show()

LSTM 활용

  • 데이터 정규화 필요 (MinMaxScaler)
  • 과거 데이터를 기반으로 미래 값 예측 가능

결론

ARIMA → 단기적인 예측에 강점, 간단한 시계열 분석에 적합
SARIMA → 계절성을 반영한 시계열 분석에 적합
LSTM → 복잡한 패턴을 학습하고 장기 예측에 강력한 성능 발휘

적절한 시계열 모델을 선택하면 데이터 패턴을 분석하고 미래를 효과적으로 예측할 수 있다.

출처: https://gangdonggil.tistory.com/1411 [개발_노트:티스토리]

 

아래는 요청하신 PPTA 웹진 2022년 11월호 “딥러닝 기반 시계열 예측(Time Series Forecasting) 모델 동향” 페이지 내용을 공적이고 전문적인 스타일간결하게 정리한 것입니다.


📌 개요

  • 주제: 금융, 기후, 의료, 교통 등 다양한 분야에서 활용되는 시계열 데이터 예측의 최근 딥러닝 모델 동향. 전통적 통계모델(ARIMA, VAR)과 대비해 RNN 계열, Neural ODE/CDE, Transformer 계열, GCN 계열 모델을 중심으로 소개. 정부조달기술진흥협회
  • 저자: 임선규 과장 (금융결제원), 개인 의견임을 명시. 정부조달기술진흥협회

I. 서론

  • 시계열 데이터 예측은 오랜 전통의 연구 분야로,
    • ARIMAVAR 같은 통계 기반 모델에 더해
    • RNN, GRU, LSTM 등 딥러닝 기반 모델 연구가 활발히 이루어짐 정부조달기술진흥협회
  • 논문 구조: RNN 계열(II장), ODE/CDE 기반(III장), Transformer 기반(IV장), 공간-시간 정보 활용 GCN 계열(V장), 적용 환경(VI장) 순.

II. RNN 계열 모델

  • RNN은 순환적 구조로 시계열 데이터를 다루지만, 장기 의존성 문제(long‑term dependency)가 존재 정부조달기술진흥협회
  • 이를 보완한 LSTM, GRU
    • LSTM: forget/input/output 게이트 구조로 장기 패턴 학습 가능
    • GRU: reset/update 게이트, 파라미터 수 적고 학습 속도 빠름 정부조달기술진흥협회

III. ODE / CDE 기반 모델

  • 기존 RNN·LSTM·GRU는 불연속 데이터 기반이며, Neural ODENeural CDE
    • 연속적 hidden state dynamics를 미분 방정식 개념으로 모델링
    • Neural ODE: 연속 모델링과 메모리 효율성을 동시에 확보 (역전파 시 ODE solver 사용) 정부조달기술진흥협회
    • ODE‑RNN: 불규칙 시계열 처리에 적합
    • Neural CDE: controlled differential equations 기반으로 irregular time series에 특화 정부조달기술진흥협회

IV. Transformer 계열 모델

  • Transformer는 자연어처리 기반 기술이나,
    • Seq2Seq + Attention 구조를 통해 장기 의존성 문제 개선
    • Transformer 계열은 시계열 예측에 더 최적화된 구조로 발전 중 정부조달기술진흥협회

V. GCN 계열 모델


VI. 모델 활용 환경

  • 대부분 모델은 PyTorch 등 개발 프레임워크 패키지GitHub 오픈 소스 코드 형태로 제공됨
  • 실험 환경 구축 시 접근성과 활용 사례가 풍부하며, 실제 데이터 분석 및 예측 실험에 바로 적용 가능 정부조달기술진흥협회

✅ 요약 정리

모델 계열특징 요약
RNN·LSTM·GRU 순차적 예측, 장기 의존성 문제 존재 → 게이트 방식으로 보완
Neural ODE/CDE continuous hidden state modeling, 메모리 효율성과 irregular data 처리 우수
Transformer Attention 기반, 장기 의존성 문제 해결, NLP에서 시계열 예측으로 확장
GCN 공간+시간 정보 함께 활용, 교통 등 그래프 특성 데이터 예측에 강점
 

💡 추천 학습 방향

  • 직접 코딩 실험 또는 Kaggle 등 예측 대회 데이터 사용하여,
    • 실습 중심으로 LSTM, Transformer, GCN 기반 시계열 모델을 비교해 보는 것 권장합니다.
  • 논문이나 모델별 GitHub 구현 코드 확인 후, 적용 사례 중심으로 분석해보시면 이해가 훨씬 수월하실 것입니다.

추가적으로 알고 싶은 부분이나, 예제 코드·모델 추천 요청 있으면 언제든지 말씀해 주세요.

'study' 카테고리의 다른 글

문제점: Tomcat 버전 정보 노출  (0) 2025.05.09
Mac 내장 Apache로 웹서버 운영하기  (0) 2025.01.23
jquery formData();  (0) 2023.09.25
SVN CLEANUP 옵션설명  (0) 2023.04.14
jar 압축 / 해제  (0) 2023.03.09

 

해당 블로그 글([보안가이드] Tomcat Version 정보 노출 방지)에서는 Apache Tomcat의 버전 정보가 외부에 노출될 경우 보안상 위험이 증가하므로, 이를 방지하기 위한 설정 방법을 상세히 설명하고 있습니다.온실 속 선인장 🌵🌵


🔍 문제점: Tomcat 버전 정보 노출

Tomcat을 기본 설정으로 사용할 경우, 다음과 같은 방식으로 버전 정보가 노출될 수 있습니다:

이러한 정보는 공격자가 특정 버전의 알려진 취약점을 이용하여 시스템을 공격하는 데 활용될 수 있습니다.온실 속 선인장 🌵🌵


🔧 해결 방법: 버전 정보 제거 설정

1. Response Header에서 버전 정보 제거

Tomcat의 server.xml 파일에서 <Connector> 태그에 server 속성을 추가하여 서버 정보를 변경합니다:온실 속 선인장 🌵🌵+1LainyZine: Programmer's Guide+1

xml
복사편집
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" server="Web Server" />

이렇게 설정하면 Response Header의 Server 값이 "Web Server"로 표시되어 실제 Tomcat 버전 정보가 숨겨집니다.온실 속 선인장 🌵🌵

2. 기본 에러 페이지에서 버전 정보 제거

{Tomcat 설치 경로}/lib/org/apache/catalina/util 디렉토리 내의 ServerInfo.properties 파일을 수정하거나 생성하여 다음과 같이 설정합니다:온실 속 선인장 🌵🌵

properties
복사편집
server.info=Web Server server.number=

이 설정을 적용하면 기본 에러 페이지에 표시되는 버전 정보가 "Web Server"로 변경됩니다.온실 속 선인장 🌵🌵

3. 에러 리포트에서 버전 정보 제거

server.xml 파일에 다음과 같은 <Valve> 설정을 추가하여 에러 리포트에 서버 정보가 노출되지 않도록 합니다:온실 속 선인장 🌵🌵

xml
복사편집
<Valve className="org.apache.catalina.valves.ErrorReportValve" showReport="false" showServerInfo="false" />

이 설정을 통해 에러 리포트에서 상세한 서버 정보와 보고서가 표시되지 않도록 할 수 있습니다.


✅ 결론

Tomcat의 버전 정보 노출은 보안상 심각한 위협이 될 수 있습니다. 위에서 설명한 설정을 통해 불필요한 정보 노출을 방지하고, 시스템의 보안을 강화하는 것이 중요합니다.

자세한 내용은 원문을 참고하시기 바랍니다: https://happylie.tistory.com/180

https://steady-record.tistory.com/entry/WebSocket

 

[Spring] WebSocket 기초 예제

Socket? WebSocket?🔎 - Socket Socket는 네트워크 통신을 위한 도구로, 무전기나 전화기와 같이 프로그램 간에 데이터를 주고받을 수 있게 해준다. 이는 특정한 인스턴스가 아닌 통신 규격을 나타낸다.

steady-record.tistory.com

[Spring] WebSocket 기초 예제

2023. 12. 1. 22:01

Socket? WebSocket?🔎

 

- Socket

  • Socket는 네트워크 통신을 위한 도구로, 무전기나 전화기와 같이 프로그램 간에 데이터를 주고받을 수 있게 해준다. 이는 특정한 인스턴스가 아닌 통신 규격을 나타낸다.
  • 프로그래밍 언어들은 이미 Socket을 구현해두어 프로그래머가 손쉽게 네트워크 통신을 구현할 수 있다.
  • 브라우저도 네트워크를 통해 데이터를 주고받을 때 Socket를 사용하여 통신한다. 그러나 최근에는 웹 기술의 발전으로 인해 Socket 사용률이 줄어들었다.

- WebSocket

  • WebSocket은 웹상에서 동작하는 Socket으로, 기존의 웹 통신 방식과는 다르게 양방향 통신을 지원한다.
  • Ajax와 유사한 면이 있지만 Ajax는 단방향 통신에 주로 사용되며, WebSocket은 웹 페이지에서 서버로부터 데이터를 받는 것뿐만 아니라, 웹 페이지에서 서버로 데이터를 보내는 양방향 통신이 가능하다. 이를 통해 다양한 실시간 데이터를 웹 애플리케이션에 적용할 수 있다.
  • WebSocket은 기존의 웹 통신 방식보다 빠르고 효율적이며, 실시간 기능을 제공하기 위해 널리 사용되고 있다.

WebSocket 프로젝트🔎

WebSocket를 이해할 수 있는 프로젝트를 진행한다.

 

- 파일 구성

상위 패키지(폴더) 하위 패키지(폴더) 파일명
src/main/java com.test.controller SocketController.java
  com.test.server SocketServer.java
WEB-INF views test.jsp

 


- 환경 설정

1. 자바 및 스프링 버전 변경 - pom.xml 

2. 의존성 추가

 

WebSocket를 사용하기 위한 의존성을 추가한다.

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-websocket</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>
	
		<dependency>
			<groupId>javax.websocket</groupId>
			<artifactId>javax.websocket-api</artifactId>
			<version>1.1</version>
		</dependency>

 


- SocketServer.java

WebSoket 으로 클라이언트와 서버가 통신할 때는 Controller의 역할은 view를 띄어주는 역할만 존재한다. 

package com.test.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class SocketController {
	
	@GetMapping(value = "/test.do")
	public String test(Model model) {
		return "test";
	}
}

 


📢 잠깐 여기서, 클라이언트와 SocketServer 의 관계

1. 일방적인 데이터 송수신

웹 소켓을 사용하면 양방향 통신이 가능하며, 클라이언트와 서버 간에 언제든 데이터를 주고 받을 수 있다. 이는 HTTP와는 다르게 일방적인 클라이언트에서 요청하고 서버가 응답하는 구조가 아닌, 양쪽에서 언제든 데이터를 주고 받을 수 있다.

 

2. 클라이언트의 연결 시작

Socketserver는 클라이언트가 누군지 모르기 때문에  웹 소켓에서는 클라이언트가 먼저 연결을 시작해야 한다. 이를 통해 클라이언트는 서버에게 연결을 요청하고, 서버는 해당 연결을 수락하여 양방향 통신이 가능한 상태가 된다.


3. @ServerEndpoint

@ServerEndpoint 어노테이션은 Java에서 웹 소켓 엔드포인트를 정의하는 데 사용된다. 종단점은 웹 소켓 서버에서 클라이언트의 요청을 처리하고, 연결을 관리하는 핵심 구성 요소이다.

 

4. 연결 지속성

한 쪽에서 일방적으로 연결을 끊어버리지 않는 한, 웹 소켓 연결은 계속해서 지속된다. 이 특징은 HTTP와 달리 지속적인 양방향 통신이 가능하도록 한다.


- WebSocket 연결하기

위 2번 특징으로 클라이언트(jsp)에서 먼저 연결 시도를 한다.

화면의 연결 버튼을 누르면 4가지 단계가 이뤄져야한다.

1. 소켓 생성

2. 서버 접속(연결)

3. 통신

4. 서버 접속 해제(종료)

 

test.jsp

test.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>WebSocketTest</title>
<link rel="stylesheet" href="https://me2.do/5BvBFJ57">
</head>
<body>
	<!-- test.jsp -->
	<h1>WebSocket <small>연결 테스트</small></h1>
	
	<div>
		<button type="button" class="in" id="btnConnect">연결하기</button>
		<button type="button" class="out" id="btnDisConnect">종료하기</button>
	</div>
	<hr>
	
	<div>
		<input type="text" class="long" id="msg">
		<button type="button" id="btnMsg">보내기</button>
	</div>
	
	<div class="message full"></div>
	<script src="https://code.jquery.com/jquery-1.12.4.js" ></script>
</body>
</html>

 

 

test.jsp (JavaScript)

	<script>
		const url = 'ws://localhost:8090/socket/testserver.do';
		
		let ws; //웹 소켓 참조 변수
        
		$('#btnConnect').click(function() {
			
			ws = new WebSocket(url);
			
			ws.onopen = function(evt) {
				log('서버와 연결하였습니다.');
			};
			
			ws.onclose = function(evt) {
				log('서버와 연결이 종료되었습니다.');
			};
			
			ws.onmessage = function(evt) {
				log(evt.data);
			};
			
			ws.onerror = function(evt) {
				log('에러가 발생했습니다.' + evt);
			};
		});
		
		$('#btnDisConnect').click(function() {
			ws.close();
			log('서버와 연결 종료를 시도합니다.');
			
		});
		
		function log(msg) {
			$('.message').prepend(`
				<div>[\${new Date().toLocaleTimeString()}] \${msg}</div>		
			`);
		}
		
		$('#btnMsg').click(function() {
			ws.send($('#msg').val());
			log('메시지를 전송했습니다.');
			
			$('#msg').val('');
		});
		
	</script>

 

 

const url = 'ws://localhost:8090/socket/testserver.do';

클라이언트와 서버 연결 할 때는 'http' 이 아닌 WebSocket 의 약자인 'ws' 프로토콜을 사용한다.

위 주소 중 testserver.do는 서버의 @ServerEndpoint 와 연결하여 클라이언트에서 서버를 매핑하는 역할을 한다. 

 

ws = new WebSocket(url);

WebSocket 메소드를 사용하면 소켓 생성과 동시에 서버에 접속 시도한다.

 

웹 소켓 이벤트

웹 소켓에는 각종 행위들이 이벤트로 구현되어있어, 클라이언트는 비동기 방식으로 다른 업무를 하다가 이벤트로 연락 오는 것을 확인할 수 있다.

ws.onopen : 서버측에서 소켓 연결을 받아들이고 연결이 되는 순간 이벤트가 발생한다.

ws.onclose : 웹 소켓 연결이 닫힐 때 발생한다.

ws.onmessage : 웹 소켓으로부터 메시지를 수신했을 때 발생한다.

ws.onerror : 웹 소켓 통신 중 에러가 발생했을 때 호출된다.

 

 

ws.send($('#msg').val());

연결된 서버에게 메시지를 전송할 때는 ws.send('전달할 메시지') 를 사용한다.

메시지를 서버에 전송하고나면 다음 입력을 위해 초기화를 한다.

 

SocketServer.java

package com.test.server;

@ServerEndpoint("/testserver.do")
public class SocketServer {

	@OnOpen
	public void handelOpen() {
		System.out.println("클라이언트가 접속했습니다.");
	}
	
	@OnClose
	public void handleClose() {
		System.out.println("클라이언트가 종료했습니다.");
	}
	
	@OnMessage
	public String handleMasseage(String msg) {
		System.out.println("클라이언트가 보낸 메시지: " + msg);
		
		return "(응답)" + msg;
	}
	
	@OnError
	public void handleError(Throwable e) {
		System.out.println("에러 발생 " + e.getMessage());
	}
	
}

 

@ServerEndpoint("/testserver.do")

위에서 클라이언트가 testserver.do 를 매핑하였다.

 

@OnMessage

서버 측에서 웹 소켓 이벤트를 처리하기 위해 어노테이션을 사용한다.

이 어노테이션은 클라이언트가 서버에게 메시지를 전송했을 때(ws.send) 반응하는 이벤트이다.  전달한 메시지가 매개변수로 설정된다.

 

보통 메소드 이름은 'handle + 이벤트' 으로 작명한다.

 

연결 및 메시지 전송 모습

 

연결, 전송, 연결종료
공유하기
게시글 관리
구독하기
 

Geolocation API로 실시간 위치 정보 가져오기

2024. 9. 8. 18:58· JavaScript

https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API

 

Geolocation API - Web APIs | MDN

The Geolocation API allows the user to provide their location to web applications if they so desire. For privacy reasons, the user is asked for permission to report location information.

developer.mozilla.org

1. Geolocation API란?

Geolocation API는 웹사이트가 사용자의 기기에서 위치 정보를 요청할 수 있도록 해주는 기능입니다.

위치 정보는 사용자의 허락을 받아야만 가져올 수 있습니다.

2. 위치 정보 가져오기: getCurrentPosition()

위치 정보를 한 번만 가져오려면 getCurrentPosition() 메서드를 사용합니다.

이 메서드는 성공 시 실행될 함수(필수), 실패 시 실행될 함수(필수), 그리고 추가 설정 옵션(선택)을 인수로 받습니다.

JAVASCRIPT

 

navigator.geolocation.getCurrentPosition(
    (position) => {
        console.log("위도: " + position.coords.latitude);
        console.log("경도: " + position.coords.longitude);
    },
    (error) => {
        console.error("오류 발생: ", error);
    }
);

3. 위치 정보 객체 (position) 자세히 알아보기

성공적으로 위치 정보를 가져오면 position 객체가 반환됩니다.

이 객체에는 사용자 기기의 여러 위치 정보가 들어 있습니다.

  • coords.latitude: 위도 (ex: 37.5172)
  • coords.longitude: 경도 (ex: 127.0473)
  • coords.altitude: 고도, 미터 단위 (사용할 수 없으면 null).
  • coords.accuracy: 위치의 정확도, 미터 단위.
  • coords.altitudeAccuracy: 고도의 정확도 (사용할 수 없으면 null).
  • coords.heading: 기기가 가리키고 있는 방향 (정지 상태일 경우 null).
  • coords.speed: 기기의 이동 속도, m/s 단위 (정지 상태일 경우 null)
JAVASCRIPT

 

navigator.geolocation.getCurrentPosition(
    (position) => {
        const { latitude, longitude, accuracy, altitude, heading, speed } = position.coords;
        console.log(`위도: ${latitude}, 경도: ${longitude}`);
        console.log(`정확도: ${accuracy}m`);
        console.log(`고도: ${altitude !== null ? altitude + "m" : "정보 없음"}`);
        console.log(`방향: ${heading !== null ? heading + "°" : "정지 상태"}`);
        console.log(`속도: ${speed !== null ? speed + "m/s" : "정지 상태"}`);
    }
);

4. 오류 처리

위치 정보를 가져오는 중 발생하는 오류는 크게 세 가지로 나눌 수 있습니다.

  • PERMISSION_DENIED: 사용자가 위치 정보 접근을 거부한 경우.
  • POSITION_UNAVAILABLE: 기기의 위치 정보를 사용할 수 없는 경우.
  • TIMEOUT: 위치 정보를 가져오는 시간이 초과된 경우.
JAVASCRIPT

 

navigator.geolocation.getCurrentPosition(
    (position) => {
        // 위치 정보를 성공적으로 가져옴
    },
    (error) => {
        switch (error.code) {
            case error.PERMISSION_DENIED:
                alert("위치 정보 접근이 거부되었습니다.");
                break;
            case error.POSITION_UNAVAILABLE:
                alert("위치 정보를 사용할 수 없습니다.");
                break;
            case error.TIMEOUT:
                alert("위치 정보를 가져오는 데 시간이 너무 오래 걸렸습니다.");
                break;
            default:
                alert("알 수 없는 오류가 발생했습니다.");
        }
    }
);

5. 위치 요청 시 옵션 설정하기

getCurrentPosition() 메서드는 추가적인 옵션을 설정할 수 있습니다.

  • enableHighAccuracy: 위치 정보의 정확도를 높일지 여부(기본값 false)
    true로 설정하면 더 정확한 위치 정보를 제공하지만, 배터리 소모량이 증가할 수 있습니다.
  • timeout: 위치 정보를 가져오는 데 걸리는 최대 시간 (밀리초 단위).
  • maximumAge: 이전 위치 정보를 얼마 동안 캐시할지 (밀리초 단위). 0으로 설정 시 항상 최신 정보를 요청합니다.
JAVASCRIPT

 

navigator.geolocation.getCurrentPosition(
    (position) => {
        console.log(`위도: ${position.coords.latitude}, 경도: ${position.coords.longitude}`);
    },
    (error) => {
        console.error("위치 정보를 가져오는 중 오류 발생: ", error);
    },
    {
        enableHighAccuracy: true,  // 고정밀도 위치 요청
        timeout: 5000,             // 5초 이내에 위치 정보를 가져오지 않으면 오류 처리
        maximumAge: 0              // 캐시된 위치 정보 사용 안 함
    }
);

 

https://hy-un.tistory.com/entry/Geolocation-API%EB%A1%9C-%EC%8B%A4%EC%8B%9C%EA%B0%84-%EC%9C%84%EC%B9%98-%EC%A0%95%EB%B3%B4-%EA%B0%80%EC%A0%B8%EC%98%A4%EA%B8%B0

Geolocation API 사용하기

보안 컨텍스트: 이 기능은 일부 또는 모든 지원 브라우저 보안 컨텍스트 (HTTPS)에서만 사용할 수 있습니다.

Geolocation API는 사용자의 현재 위치를 가져오는 API로, 지도에 사용자 위치를 표시하는 등 다양한 용도로 사용할 수 있습니다. 이 글에서는 Geolocation API의 기초 사용법을 설명합니다.

geolocation 객체

Geolocation API navigator.geolocation 객체를 통해 사용할 수 있습니다.

geolocation 객체가 존재하는 경우 위치 정보 서비스를 지원하는 것입니다. 객체의 존재 여부는 다음과 같이 알아낼 수 있습니다.

jsCopy to Clipboard
if ("geolocation" in navigator) {
  /* 위치정보 사용 가능 */
} else {
  /* 위치정보 사용 불가능 */
}

현재 위치 가져오기

사용자의 현재 위치는 getCurrentPosition() 메서드를 호출해서 가져올 수 있습니다. 이 메서드는 사용자의 위치를 탐지하는 비동기 요청을 시작하고, 위치 관련 하드웨어에 최신 정보를 요청하며, 위치를 알아낸 후 주어진 콜백 함수를 호출합니다. 선택적으로, 오류가 발생하면 호출할 콜백을 두 번째 매개변수로 지정할 수 있습니다. 또 다른 선택 사항인 세 번째 매개변수는 위치 정보의 최대 수명, 요청의 최대 대기시간, 고정밀 위치정보 여부 등의 옵션을 담은 객체입니다.

참고: getCurrentPosition()의 기본 설정에서는 정밀도가 낮더라도 최대한 빠르게 응답을 반환하므로, 정확도보다 속도가 중요한 상황에서 유용합니다. 예를 들어, GPS 기능을 가진 장비는 보정 과정에 수 분이 걸릴 수도 있으므로 그동안 IP 위치와 Wi-Fi 등 정확하지 않은 출처에 기반한 위치 정보를 반환할 수 있습니다.

jsCopy to Clipboard
navigator.geolocation.getCurrentPosition((position) => {
  doSomething(position.coords.latitude, position.coords.longitude);
});

위의 예제는 사용자 위치가 확인되면 doSomething() 함수를 실행합니다.

현재 위치 추적하기

장치의 이동이나 위치 정밀도 향상으로 인해 위치 정보가 바뀔 때 호출할 콜백 함수를 watchPosition() 메서드로 설정할 수 있습니다. 이 메서드의 매개변수는 getCurrentPosition()과 같습니다. 이 콜백은 여러 번 호출될 수 있으므로 브라우저가 사용자의 움직임에 따라 위치를 업데이트하거나, 고정밀 위치 기술을 적용해 보다 정밀한 위치를 표시할 수 있습니다. getCurrentPosition()과 마찬가지로 선택 사항인 오류 콜백 역시 여러 번 호출될 수 있습니다.

참고: getCurrentPosition()을 먼저 호출하지 않아도 watchPosition()을 사용할 수 있습니다.

jsCopy to Clipboard
const watchID = navigator.geolocation.watchPosition((position) => {
  doSomething(position.coords.latitude, position.coords.longitude);
});

watchPosition() 메서드는 위치 추적 요청을 식별할 수 있는 고유 숫자를 반환합니다. 이 숫자를 clearWatch() 메서드에 전달하면 해당 위치 추적을 종료할 수 있습니다.

jsCopy to Clipboard
navigator.geolocation.clearWatch(watchID);

응답 미세 조정

getCurrentPosition() watchPosition() 둘 다 성공 콜백, 실패 콜백, 그리고 옵션 객체를 받을 수 있습니다.

이 옵션 객체로는 고정밀도 활성화 여부, 위치 정보의 최대 수명(수명이 끝나기 전에는 이전에 반환했던 위치 정보를 저장했다가, 같은 요청을 또 받으면 그대로 반환합니다), 그리고 위치 정보 요청의 응답을 기다릴 최대 대기시간을 지정할 수 있습니다.

옵션 객체를 사용한 watchPosition의 호출 예시는 다음과 같습니다.

jsCopy to Clipboard
function success(position) {
  doSomething(position.coords.latitude, position.coords.longitude);
}

function error() {
  alert("죄송합니다. 위치 정보를 사용할 수 없습니다.");
}

const options = {
  enableHighAccuracy: true,
  maximumAge: 30000,
  timeout: 27000,
};

const watchID = navigator.geolocation.watchPosition(success, error, options);

위치 표현

사용자의 위치는 GeolocationPosition 객체 인스턴스와, 그 안의 GeolocationCoordinates 객체 인스턴스로 표현됩니다.

GeolocationPosition은 오직 두 가지, GeolocationCoordinates 인스턴스를 가진 coords 속성과, 위치 정보의 기록 시점을 타임스탬프(Unix 시간, 밀리초)로 나타내는 timestamp 속성만 갖습니다.

GeolocationCoordinates 인스턴스는 여러 속성을 갖지만, 그중 가장 많이 쓰게 될 두 가지는 지도 위에 위치를 표시할 때 필요한 latitude와 longitude입니다. 따라서 대부분의 Geolocation 성공 콜백은 아래와 같이 꽤 간단한 형태입니다.

jsCopy to Clipboard
function success(position) {
  const latitude = position.coords.latitude;
  const longitude = position.coords.longitude;

  // 위도와 경도를 사용해 작업 수행
}

그러나 GeolocationCoordinates 객체에서 고도, 속도, 장치의 방향, 위경도와 고도의 오차범위 등 다른 다양한 정보도 가져올 수 있습니다.

오류 처리

getCurrentPosition() 또는 watchPosition()에 오류 콜백을 제공한 경우, 콜백은 첫 번째 매개변수로 GeolocationPositionError 객체를 받습니다. 해당 객체는 오류의 유형을 나타내는 code 속성과, 사람이 읽을 수 있는 형태로 오류 코드의 뜻을 설명한 message 속성을 갖습니다.

다음 형태로 사용할 수 있습니다.

jsCopy to Clipboard
function errorCallback(error) {
  alert(`ERROR(${error.code}): ${error.message}`);
}

예제

다음 예제는 Geolocation API를 사용해 사용자의 위경도를 가져오는 데 성공하면, 사용자의 위치로 향하는 openstreetmap.org 링크를 생성하고 하이퍼링크에 지정합니다.

HTML

htmlCopy to Clipboardplay
<button id="find-me">내 위치 보기</button><br />
<p id="status"></p>
<a id="map-link" target="_blank"></a>

JavaScript

jsCopy to Clipboardplay
function geoFindMe() {
  const status = document.querySelector("#status");
  const mapLink = document.querySelector("#map-link");

  mapLink.href = "";
  mapLink.textContent = "";

  function success(position) {
    const latitude = position.coords.latitude;
    const longitude = position.coords.longitude;

    status.textContent = "";
    mapLink.href = `https://www.openstreetmap.org/#map=18/${latitude}/${longitude}`;
    mapLink.textContent = `위도: ${latitude} °, 경도: ${longitude} °`;
  }

  function error() {
    status.textContent = "현재 위치를 가져올 수 없음";
  }

  if (!navigator.geolocation) {
    status.textContent = "브라우저가 위치 정보를 지원하지 않음";
  } else {
    status.textContent = "위치 파악 중…";
    navigator.geolocation.getCurrentPosition(success, error);
  }
}

document.querySelector("#find-me").addEventListener("click", geoFindMe);



Socket? WebSocket?🔎

 

- Socket

  • Socket는 네트워크 통신을 위한 도구로, 무전기나 전화기와 같이 프로그램 간에 데이터를 주고받을 수 있게 해준다. 이는 특정한 인스턴스가 아닌 통신 규격을 나타낸다.
  • 프로그래밍 언어들은 이미 Socket을 구현해두어 프로그래머가 손쉽게 네트워크 통신을 구현할 수 있다.
  • 브라우저도 네트워크를 통해 데이터를 주고받을 때 Socket를 사용하여 통신한다. 그러나 최근에는 웹 기술의 발전으로 인해 Socket 사용률이 줄어들었다.

- WebSocket

  • WebSocket은 웹상에서 동작하는 Socket으로, 기존의 웹 통신 방식과는 다르게 양방향 통신을 지원한다.
  • Ajax와 유사한 면이 있지만 Ajax는 단방향 통신에 주로 사용되며, WebSocket은 웹 페이지에서 서버로부터 데이터를 받는 것뿐만 아니라, 웹 페이지에서 서버로 데이터를 보내는 양방향 통신이 가능하다. 이를 통해 다양한 실시간 데이터를 웹 애플리케이션에 적용할 수 있다.
  • WebSocket은 기존의 웹 통신 방식보다 빠르고 효율적이며, 실시간 기능을 제공하기 위해 널리 사용되고 있다.

WebSocket 프로젝트🔎

WebSocket를 이해할 수 있는 프로젝트를 진행한다.

 

- 파일 구성

상위 패키지(폴더) 하위 패키지(폴더) 파일명
src/main/java com.test.controller SocketController.java
  com.test.server SocketServer.java
WEB-INF views test.jsp

 


- 환경 설정

1. 자바 및 스프링 버전 변경 - pom.xml 

2. 의존성 추가

 

WebSocket를 사용하기 위한 의존성을 추가한다.

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-websocket</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>
	
		<dependency>
			<groupId>javax.websocket</groupId>
			<artifactId>javax.websocket-api</artifactId>
			<version>1.1</version>
		</dependency>

 


- SocketServer.java

WebSoket 으로 클라이언트와 서버가 통신할 때는 Controller의 역할은 view를 띄어주는 역할만 존재한다. 

package com.test.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class SocketController {
	
	@GetMapping(value = "/test.do")
	public String test(Model model) {
		return "test";
	}
}

 


📢 잠깐 여기서, 클라이언트와 SocketServer 의 관계

1. 일방적인 데이터 송수신

웹 소켓을 사용하면 양방향 통신이 가능하며, 클라이언트와 서버 간에 언제든 데이터를 주고 받을 수 있다. 이는 HTTP와는 다르게 일방적인 클라이언트에서 요청하고 서버가 응답하는 구조가 아닌, 양쪽에서 언제든 데이터를 주고 받을 수 있다.

 

2. 클라이언트의 연결 시작

Socketserver는 클라이언트가 누군지 모르기 때문에  웹 소켓에서는 클라이언트가 먼저 연결을 시작해야 한다. 이를 통해 클라이언트는 서버에게 연결을 요청하고, 서버는 해당 연결을 수락하여 양방향 통신이 가능한 상태가 된다.


3. @ServerEndpoint

@ServerEndpoint 어노테이션은 Java에서 웹 소켓 엔드포인트를 정의하는 데 사용된다. 종단점은 웹 소켓 서버에서 클라이언트의 요청을 처리하고, 연결을 관리하는 핵심 구성 요소이다.

 

4. 연결 지속성

한 쪽에서 일방적으로 연결을 끊어버리지 않는 한, 웹 소켓 연결은 계속해서 지속된다. 이 특징은 HTTP와 달리 지속적인 양방향 통신이 가능하도록 한다.


- WebSocket 연결하기

위 2번 특징으로 클라이언트(jsp)에서 먼저 연결 시도를 한다.

화면의 연결 버튼을 누르면 4가지 단계가 이뤄져야한다.

1. 소켓 생성

2. 서버 접속(연결)

3. 통신

4. 서버 접속 해제(종료)

 

test.jsp

test.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>WebSocketTest</title>
<link rel="stylesheet" href="https://me2.do/5BvBFJ57">
</head>
<body>
	<!-- test.jsp -->
	<h1>WebSocket <small>연결 테스트</small></h1>
	
	<div>
		<button type="button" class="in" id="btnConnect">연결하기</button>
		<button type="button" class="out" id="btnDisConnect">종료하기</button>
	</div>
	<hr>
	
	<div>
		<input type="text" class="long" id="msg">
		<button type="button" id="btnMsg">보내기</button>
	</div>
	
	<div class="message full"></div>
	<script src="https://code.jquery.com/jquery-1.12.4.js" ></script>
</body>
</html>

 

 

test.jsp (JavaScript)

	<script>
		const url = 'ws://localhost:8090/socket/testserver.do';
		
		let ws; //웹 소켓 참조 변수
        
		$('#btnConnect').click(function() {
			
			ws = new WebSocket(url);
			
			ws.onopen = function(evt) {
				log('서버와 연결하였습니다.');
			};
			
			ws.onclose = function(evt) {
				log('서버와 연결이 종료되었습니다.');
			};
			
			ws.onmessage = function(evt) {
				log(evt.data);
			};
			
			ws.onerror = function(evt) {
				log('에러가 발생했습니다.' + evt);
			};
		});
		
		$('#btnDisConnect').click(function() {
			ws.close();
			log('서버와 연결 종료를 시도합니다.');
			
		});
		
		function log(msg) {
			$('.message').prepend(`
				<div>[\${new Date().toLocaleTimeString()}] \${msg}</div>		
			`);
		}
		
		$('#btnMsg').click(function() {
			ws.send($('#msg').val());
			log('메시지를 전송했습니다.');
			
			$('#msg').val('');
		});
		
	</script>

 

 

const url = 'ws://localhost:8090/socket/testserver.do';

클라이언트와 서버 연결 할 때는 'http' 이 아닌 WebSocket 의 약자인 'ws' 프로토콜을 사용한다.

위 주소 중 testserver.do는 서버의 @ServerEndpoint 와 연결하여 클라이언트에서 서버를 매핑하는 역할을 한다. 

 

ws = new WebSocket(url);

WebSocket 메소드를 사용하면 소켓 생성과 동시에 서버에 접속 시도한다.

 

웹 소켓 이벤트

웹 소켓에는 각종 행위들이 이벤트로 구현되어있어, 클라이언트는 비동기 방식으로 다른 업무를 하다가 이벤트로 연락 오는 것을 확인할 수 있다.

ws.onopen : 서버측에서 소켓 연결을 받아들이고 연결이 되는 순간 이벤트가 발생한다.

ws.onclose : 웹 소켓 연결이 닫힐 때 발생한다.

ws.onmessage : 웹 소켓으로부터 메시지를 수신했을 때 발생한다.

ws.onerror : 웹 소켓 통신 중 에러가 발생했을 때 호출된다.

 

 

ws.send($('#msg').val());

연결된 서버에게 메시지를 전송할 때는 ws.send('전달할 메시지') 를 사용한다.

메시지를 서버에 전송하고나면 다음 입력을 위해 초기화를 한다.

 

SocketServer.java

package com.test.server;

@ServerEndpoint("/testserver.do")
public class SocketServer {

	@OnOpen
	public void handelOpen() {
		System.out.println("클라이언트가 접속했습니다.");
	}
	
	@OnClose
	public void handleClose() {
		System.out.println("클라이언트가 종료했습니다.");
	}
	
	@OnMessage
	public String handleMasseage(String msg) {
		System.out.println("클라이언트가 보낸 메시지: " + msg);
		
		return "(응답)" + msg;
	}
	
	@OnError
	public void handleError(Throwable e) {
		System.out.println("에러 발생 " + e.getMessage());
	}
	
}

 

@ServerEndpoint("/testserver.do")

위에서 클라이언트가 testserver.do 를 매핑하였다.

 

@OnMessage

서버 측에서 웹 소켓 이벤트를 처리하기 위해 어노테이션을 사용한다.

이 어노테이션은 클라이언트가 서버에게 메시지를 전송했을 때(ws.send) 반응하는 이벤트이다.  전달한 메시지가 매개변수로 설정된다.

 

보통 메소드 이름은 'handle + 이벤트' 으로 작명한다.

 

연결 및 메시지 전송 모습

 

연결, 전송, 연결종료

[강의 출처] opentutorials.org/course/3084/18894

 

웹서버 운영하기 : 맥 - 생활코딩

자신의 운영체제에 맞는 수업을 찾아서 학습해주세요. 맥에 웹서버 설치하기 웹서버와 http  웹브라우저와 웹서버의 통신 --- 맥에 웹서버 설치 이번 시간에는 아파치 웹서버를 맥 컴퓨터에 설치

opentutorials.org

기타 참고페이지들은 본문에 적어두었다.


 

생활코딩 Web1 Internet편에 직접 웹서버를 운영해보는 단계가 있다.

생활코딩에서는 bitnami라는 프로그램을 통해 실습을 하는데, 아래와 같은 문구가 있는 게 아닌가.

 

"맥에는 아파치가 기본적으로 깔려있습니다. 그래서 실습을 하기가 무척 쉽습니다."

 

그래서 직접 해보기로 했다. 맥에 기본적으로 깔려 있는 아파치로 웹서버를 돌려보자!

 

Mac 내장 Apache로 웹서버 운영하기

 

우선 먼저 Google 검색을 해보았다. '맥 아파치'.

여러 글들이 많이 나오는데, 가장 많이 참조한 것은 이쪽:

xho95.github.io/macos/apache/webserver/mod_wsgi/2016/10/02/Apache-WebServer.html

88240.tistory.com/476

 

1. 맥 터미널에서 '아파치'와 'php' 버전을 확인한다.

$ apachectl -v
$ php -v

내 경우에는 아파치 2.4.41 버전, php 7.3.11 버전이 설치되어 있었다.

 

2. 아파치를 실행한다.

$ sudo apachectl start

sudo 권한이라서 password를 묻는 단계가 있다. 접속한 맥 계정 비밀번호를 입력하니 확인완료.

 

3. 브라우저에서 localhost로 접속해본다. (혹은 127.0.0.1)

사파리에서 localhost를 입력한 결과. 아파치의 기본 indexl.html 파일이다.

무사히 출력된다! 아파치 서버 활성화가 무사히 끝났다. 이제는 실습에서 만들었던 웹페이지를 띄워볼 차례.

 

4. index.html 파일 찾기 (생활코딩 Web1 웹서버 실습)

먼저 아까 출력되었던 'It works!' 파일이 어디에 있는지 알아보았다.

/Library/WebServer/Documents 디렉토리라고 한다. 해당 디렉토리로 이동 후 Finder로 열어보았다.

$ cd /Library/WebServer/Documents
$ open .

해당 디렉토리로  이동하니, 이런 이름의 파일이 있다.

/Library/WebServer/Documents/index.html

에디터로 열어보니, 아래와 같이 작성되어 있다. html문서다.

<html><body><h1>It works!</h1></body></html>

생활코딩에서 실습했던 방법을 생각해보면,

 

"htdocs 디렉터리의 파일들을 모두 삭제하고, 프로젝트 폴더에 있는 파일을 복사합니다."

여기의 index.html 파일만 교체해도 목적은 이룰 수 있을 것 같다. 시도해 본다.

index.html.en 파일을 다른 곳으로 이동하고, 내가 작성했던 파일을 복사해온 다음, 다시 localhost에 접속해보았다.

생활코딩 Web1을 들으면서 직접 만들었던 웹페이지.

성공! 링크로 연결해뒀던 페이지들 간의 이동도 의도대로 잘 실행되었다.

와이파이로 연결되어있는 스마트폰으로도 접속해보았다. 무사히 실행된다!

여기까지 하면 생활코딩 범위는 끝.

 

문제는... 이 루트는 일반적인 방법으로 파일 삭제가 안 될 뿐더러, 웹서버는 이렇게 운영하는 게 아닐 것 같다는 불안감이 든다는 것.

아까 테스트했던 파일들은 급한대로 sudo rm으로 하나하나 지웠다. 그냥 rm을 입력하면 Permission denied가 뜬다.

$ sudo rm index.html

다른 사람들은 환경설정을 어떻게 하는지 좀 더 알아보기로 했다.

 

5. 환경설정 하기: userdir 활성화하기  or  DocumentRoot 폴더 변경하기

보통은 위의 두 가지 방법으로 세팅을 하는 것 같다. 나는 이 중 userdir 활성화를 택하기로 했다.

userdir 활성화의 이점은 localhost/~username처럼 하위 링크를 얻을 수 있다는 것. 방법은 아래와 같다.

 

1. /private/etc/apache2/extra 디렉토리의 httpd-userdir.conf 파일을 편집

$ cd /private/etc/apache2/extra
$ sudo vi httpd-userdir.conf

-httpd-userdir.conf 파일에서 '# Include /private/etc/apache2/users/*.conf' 부분의 주석처리(# )를 삭제

 

마찬가지로 sudo 권한이라서 password 요청이 들어올 수 있다. 패스워드를 입력하면 vi 편집기가 뜬다.

vi 편집기에 익숙하지 않아서 조금 헤맸는데, 그래도 아래 링크 등을 참고하면 금방 적응할 수 있다.

 

coding-factory.tistory.com/505

 

[Linux] 리눅스 문서 편집기 vi 사용법 & 명령어 총정리

파일을 작성하거나 수정하기 위해서는 편집기가 필요합니다. 윈도우에서는 메모장을 편집기의 기본으로 지원하는 반면 리눅스에서는 기본 편집기로 vi 라는 편집기를 지원합니다. vi는 리눅스��

coding-factory.tistory.com

마우스를 사용하는 시스템이 아니라서, 키보드로 지금 명령을 할지 입력을 할지를 지정해줘야 한다. 커서 이동은 방향키로도 가능하다.

 

처음 써본다면 esc, i, :만 기억하면 된다.

화살표키를 이용해서 편집할 곳으로 커서를 이동한 다음, 키보드 상에서  'i'키 를 누르면 화면 맨 아랫줄에

-- INSERT --

라는 표시가 뜬다. 그 상태에서는 텍스트 입력 및 수정이 가능하다.

수정이 끝났다면 esc를 누르고 나서, ':'를 누르자. (쉬프트도 눌러줘야 한다) 그러면 다시 화면 맨 아랫줄에

:

라는 표시가 뜰 것이다. 저장하고 종료할 거라면, 아래처럼 : 뒤에 wq를 입력하고 엔터를 누르면 된다.

:wq

다시 터미널 기존 위치로 돌아갈 것이다. 혹시 강제종료하고 싶다면 wq 대신에 q!를 입력하면 된다.

 

2. /private/etc/apache2 디렉토리의 httpd.conf 파일을 편집

내 경우에는 위의 경로에 httpd.conf 파일이 있었다. 이번에는 Atom으로 열어서, 우선 아래 두 줄의 주석처리(#)를 제거하고,

LoadModule userdir_module libexec/apache2/mod_userdir.so
Include /private/etc/apache2/extra/httpd-userdir.conf

혹시 모를 ServerName 관련 오류를 피하기 위해, 주석을 제거하고 ServerName 설정을 변경하고 저장했다.

(Atom의 경우, 저장할 때 Atom Helper가 권한 관련하여 비밀번호를 요청할 수 있다.)

ServerName localhost

 

3. Sites 폴더 생성

localhost/~username 요청시 베이스로 사용할 디렉토리를 만드는 작업이다.

/Users/유저명 의 유저명 경로에 Sites 폴더를 만들면 된다. 나는 터미널을 이용했다.

$ cd ~
$ mkdir Sites

 

4. Sites 폴더에 접근 권한 주기

/private/etc/apache2/users 경로에 유저명.conf 파일을 만들어주는 작업이다. Guest.conf 파일을 복사해서 내용만 수정해도 된다.

여기서 꽤 고전했다. conf 파일의 내용은 여기를 참고해서 아래와 같이 작성했다.

<Directory "/Users/username/Sites/">
  Options Multiviews Indexes
  Require all granted
</Directory>

첫번째 줄의 username을 내 계정명으로 변경한 상태로 저장하고, 아파치를 재시작 후 접속을 시도해보았더니

 

권한이 없어서 접속할 수 없다는 오류페이지. 흔히 말하는 403 에러인듯.

 

에러 로그를 찾아보니 이런 메시지가 뜬다.

cat /var/log/apache2/error_log # 에러 로그를 찍어보자

(13)Permission denied:  AH00035: access to /~username denied (filesystem path '/Users/username/Sites') because search permissions are missing on a component of the path

구글의 도움을 받아 아래와 같은 해결법을 찾았다.

 

mytory.net/archives/3143

 

아파치 Forbidden You don’t have permission to access / on this server. 에러 해결

아파치에서 웹 서버의 폴더에 접근할 수 없어서 뜨는 에러다. 우선 아파치 환경 설정에서 해당 폴더 접근을 허용하고, 아파치 사용자에게 폴더의 실행 권한과 파일의 읽기 권한을 줘야 한다.

mytory.net

터미널로 이동해서 Sites 폴더에 권한 755를 부여했다.

$ cd ~
$ sudo chmod 755 Sites

755 권한은 소유자는 읽기, 쓰기, 실행이 가능하고, 그 외 사용자는 읽기, 실행이 가능한 타입.

여전히 Forbidden이다.

 

apple.stackexchange.com/questions/95660/how-to-fix-403-in-mac-os-x-built-in-apache/136941#136941

 

How to fix 403 in Mac OS X built-in Apache?

I'm trying to set a local environment on my new MacBook Air 13": built-in Apache with my own DocumentRoot, PHP, and MySQL. I usually update /etc/hosts just to run my local websites with a pretty

apple.stackexchange.com

httpd.conf 파일의 User 이름을 변경해서 해결할 수 있다고 한다.

User _www

해당 파일을 확인해보니 실제로 User명이 _www로 되어있다. _www를 사용자명으로 변경했다.

그리고 해당 답변자가 추가로 조언해준 보안 이슈를 해결하고 나서

Listen 80

->

Listen 127.0.0.1:80

아파치를 재실행해본다.

$ sudo apachectl restart
http://localost/~username/index.html로 접속에 성공했다

결과는 성공!

와이파이 환경 내에서 접속하려면 위의 Listen 127.0.0.1:80 부분을 Listen 80으로 그대로 두어야 한다.

[Oracle] Oracle XE에서 사용자 생성 오류, ORA-65096: 공통 사용자 또는 롤 이름이 부적합합니다. invalid common user or role name
by breezyday 2022. 8. 2.
Oracle에서 개발 및 테스트를 위해 Oracle XE를 무료로 사용할 수 있도록 배포하고 있습니다. 그러나 몇 가지 제약사항들이 있는 데 그중에 하나가 사용자 생성 시 사용자 이름에 대한 제약이 있습니다.

1. ORA-65096 : 공통 사용자 또는 롤 이름이 부적합합니다.
CREATE USER C##myuser IDENTIFIED BY pwd123;
 

Oracle XE에서는 system 계정으로 로그인해서 사용자를 만들 때, C##을 붙인 사용자는 만들 수 있지만 일반 이름은 사용할 수 없는 제약이 있습니다. 그래서 위의 SQL은 문제가 없지만 아래와 같은 일반적인 SQL문은 오류가 발생합니다.

 

CREATE USER my_user IDENTIFIED BY pwd123
DEFAULT TABLESPACE myts
 

위의 SQL을 실행하면 오류가 발생하고, 오류 메시지는 아래와 같습니다.

 

명령의 28 행에서 시작하는 중 오류 발생 -
CREATE USER my_user IDENTIFIED BY pwd123
DEFAULT TABLESPACE myts
오류 보고 -
ORA-65096: 공통 사용자 또는 롤 이름이 부적합합니다.
65096. 00000 -  "invalid common user or role name"
*Cause:    An attempt was made to create a common user or role with a name
           that was not valid for common users or roles. In addition to the
           usual rules for user and role names, common user and role names
           must consist only of ASCII characters, and must contain the prefix
           specified in common_user_prefix parameter.
*Action:   Specify a valid common user or role name.
 

2. 해결 방법
Oracle XE에서 제약을 걸어두기 위해 system 계정으로 로그인할 경우 SESSION이 잠겨진 SESSION으로 접속하게 됩니다. 그래서 아래 명령을 사용하여 SESSION 설정을 변경하면 기존의 SQL 구문들을 제약 없이 사용할 수 있습니다.

 

ALTER SESSION SET "_ORACLE_SCRIPT"=true;
 

Oracle XE에서 개발 및 테스트를 하고 있는 개발자라면 system으로 로그인해서 DDL을 다룰 경우 위 명령을 먼저 실행해주면 다른 SQL문 작업 중에도 혼돈 없이 작업을 진행할 수 있겠습니다.

 
 

오라클 테이블 스페이스(Table Space)란 무엇인가?

오라클은 데이터를 관리하는 데이터베이스입니다. 데이터를 어딘가에 저장해놓고 사용하는 시스템이라고 볼 수 있습니다. 그리고 데이터 저장 단위 중 가장 상위에 있는 단위를 테이블 스페이스라고 합니다. 데이터 저장 단위는 물리적, 논리적단위로 나눌 수 있습니다. 물리적 단위는 파일을 의미하고 논리적 단위는 데이터블록 -> 익스텐트 -> 세그먼트 -> 테이블스페이스 이렇게 나뉩니다. 데이터 블록 여러개가 모여 익스텐트 하나를 만들고, 익스텐트 여러개가 모여 하나의 세그먼트를 구성하는 식입니다.  테이블 스페이스는 가장 상위개념입니다.

 

오라클 테이블 스페이스 사용법

SQL 개발 툴이 있으시면 개발  툴을 실행시키시면 되고 없으시다면 윈도우 실행창을 열고 SQL PLUS를 실행시킨 뒤 System 아이디로 로그인한뒤 테이블 스페이스 설정 작업을 하시면 됩니다.

 

테이블 스페이스 생성

Copy
create tablespace [테이블 스페이스명]
datafile 'D:\dev\oradata' --파일경로
size 10M --초기 데이터 파일 크기 설정
autoextend on next 10M -- 초기 크기 공간을 모두 사용하는 경우 자동으로 파일의 크기가 커지는 기능
maxsize 100M -- 데이터파일이 최대로 커질 수 있는 크기 지정 기본값 = unlimited
uniform size 1M -- EXTENT 한개의 크기를 설정

 

전체 테이블 스페이스 조회

Copy
select * from dba_tablespaces;

 

전체 테이블 스페이스 경로 및 용량 조회

Copy
SELECT
A.TABLESPACE_NAME "테이블스페이스명",
A.FILE_NAME "파일경로",
(A.BYTES - B.FREE) "사용공간",
B.FREE "여유 공간",
A.BYTES "총크기",
TO_CHAR( (B.FREE / A.BYTES * 100) , '999.99')||'%' "여유공간"
FROM
(
SELECT FILE_ID,
TABLESPACE_NAME,
FILE_NAME,
SUBSTR(FILE_NAME,1,200) FILE_NM,
SUM(BYTES) BYTES
FROM DBA_DATA_FILES
GROUP BY FILE_ID,TABLESPACE_NAME,FILE_NAME,SUBSTR(FILE_NAME,1,200)
)A,
(
SELECT TABLESPACE_NAME,
FILE_ID,
SUM(NVL(BYTES,0)) FREE
FROM DBA_FREE_SPACE
GROUP BY TABLESPACE_NAME,FILE_ID
)B
WHERE A.TABLESPACE_NAME=B.TABLESPACE_NAME
AND A.FILE_ID = B.FILE_ID;

 

테이블의 테이블 스페이스 변경

Copy
alter table [테이블명] move tablespace [테이블 스페이스명]

 

테이블 스페이스 속성 변경

Copy
-- 해당 테이블스페이스의 물리적인 파일의 이름 또는 위치변경
alter tablespace rename [A] to [B]
 
-- 해당 테이블스테이스의 용량을 1024메가로 변경
alter tablespace [테이블스페이스명] add datafile [추가할데이터파일명] size 1024M;
 
-- 해당 데이터파일경로에 해당하는 테이블스페이스의 크기가 FULL이 되면 자동으로 100메가씩 증가.
alter database datafile [데이터파일경로] 'autoextend on next 100m maxsize unlmited;

 

테이블 스페이스 삭제

Copy
-- 테이블스페이스 내의 객체(테이블,인덱스등)를 전체 삭제
drop tablespace [테이블 스페이스명] include contents;
 
--테이블스페이스의 모든 세그먼트를 삭제. (데이타가 있는 테이블 스페이스 제외)
drop tablespace [테이블 스페이스명] including contents;
 
--삭제된 테이블스페이스를 참조하는 다른 테이블스페이스의 테이블로부터 참조무결성 제약 조건을 삭제
drop tablespace [테이블 스페이스명] cascade constraints;
 
--테이블 스페이스의 물리적파일까지 삭제
drop tablespace [테이블 스페이스명] including contents and datafiles;

 

테이블 스페이스 작동방식

오라클 에서는 테이블스페이스라고 불리우는 테이블이 저장될 공간을 먼저 만들고 나서 테이블을 생성합니다. 테이블에 실질적으로 저장되는 장소라고 생각하시면 됩니다. 이러한 작동방식은 각각의 테이블을 테이블스페이스별로 나누어서 관리와 퍼포먼스의 향상을 가지고 옵니다. 테이블스페이스를 생성하면 정의된 용량만큼 미리 확보한 테이블스페이스가 생성되어지고 생성되어진 테이블스페이스에 테이블의 데이타가 저장됩니다. 이렇게 설정된 데이터 스페이스에 용량이 가득차면 오라클 서버가 죽습니다. 그러므로 관리를 잘해주어야합니다. 하지만 또 테이블 스페이스마다 용량을 너무나도 크게 잡아버리면 문제가 되는게 용량을 적게 차지하고 있다고해서 가변적으로 max용량이 줄어들지는 않습니다. 고로 또 용량낭비가 되어버릴 수 있는 문제가 될 수 있습니다.. 

 

골치아프시죠?? 괜찮습니다. 테이블 스페이스는 자동으로 할당해주는 부분이 많아 크게 DBA가 아니라면 건드릴 필요가 없습니다. 아마 그럴 권한도 없으실거에요. 자동으로 할당해주는 부분을 간략하게 소개드리자면 만약 테이블 생성 시 테이블  스페이스를 지정해주지 않으면 오라클 서버에서 자동으로 지정해주기도 하고 또 오라클은 유저를 생성하면서 디폴트 테이블 스페이스를 자동으로 지정해줍니다. 테이블을 만들게 되면 그 테이블 스페이스에 들어 가게되고 그 테이블 스페이스는 테이블 스페이스 생성때 설정하는 경로에 위치하게됩니다. DBA가 아니라 오라클을 활용하는 개발자 정도라면 크게 신경쓰지 않으셔도 됩니다. 

[Oracle] 계정 및 테이블스페이스 생성

 tawoo0  2019. 5. 27. 14:59

*계정 생성 및 테이블 스페이스 생성

1. 오라클 sys 계정으로 접속한다. cmd창에서 바로 접속시

C:\>sqlplus

계정 system

비번 bims

2. 테이블 스페이스 생성

create tablespace 테이블스페이스명

datafile '/경로/테이블스페이스파일명.dbf'

size 초기용량(100m,1g 등) reuse

autoextend on next 자동증가 용량

maxsize unlimited;

ex)

create tablespace ASQ_DATA2

datafile '/home/oracle/tablespace/ASQ_DATA2.dbf'

size 300m reuse

autoextend on next 1024k

maxsize unlimited;

3. sys계정 접속후 유저계정생성

create user 유저명 identified by 패스워드 default tablespace 테이블스페이스명;

ex) create user KBN2 identified by KBN2 default tablespace ASQ_DATA2;

create user 유저명 identified by 패스워드 default tablespace 테이블스페이스명 TEMPORARY TABLESPACE 정렬용테이블스에스명;

ex) create user KBN2 identified by KBN2 default tablespace kbn_tablespace TEMPORARY TABLESPACE temp;

4.생성한계정 권한부여

grant connect, resource to 유저명;

ex)grant connect, resource to KBN2;

grant connect, resource,dba to tawoo0;

5.유저 계정 삭제시

drop user 삭제할계정 cascade

ex)drop user KBN2 cascade;

cf) 유저 계정을 삭제하고 테이블스페이스를 삭제하고 테이블스페이스 파일을 삭제하면 mysql의 drop database DB명과 같다.

6. 유저 권한 회수

revoke 권한 from 유저명;

revoke dba from user1;

7. 테이블스페이스 삭제

DROP TABLESPACE 테이블스페이스이름 INCLUDING CONTENTS;

ex) DROP TABLESPACE kbn_talbespace INCLUDING CONTENTS;

** 권한 종류

1). CONNECT : 사용자가 데이터베이스에 접속 가능하도록 하기 위해 다음과 같이 가장 기본적인 시스템 권한

8가지를 묶어 놓았습니다. (ALTER SESSION, CREATE CLUSTER, CREATE DATABASE LINK, CREATE SEQUENCE,

CREATE SESSION, CREATE SYNONYM, CREATE TABLE, CREATE VIEW)

2). RESOURCE : 사용자 객체(테이블, 뷰, 인덱스)를 생성할 수 있도록 하기위해서 시스템 권한을 묶어 놓았습니다. (CREATE CLUSTER, CREATE PROCEDURE, CREATE SEQUENCE, CREATE TABLE, CREATE TRIGGER)

3). DBA : 사용자들이 소유한 데이터베이스를 관리하고 사용자들을 작성하고 변경하고 제거할 수 있도록 하는

모든 권한을 가집니다.

참고 : http://www.gurubee.net/lecture/1160

 

* EXPORT 옵션

 

  - userid : EXPORT를 실행시키고 있는 username/password 명

  - file : 생성되는 EXPORT덤프 파일명

  - full : 전체 데이터베이스를 EXPORT할것인가의 여부 (Full Level EXPORT) (Y/N 플래그)

  - owner : EXPORT 될 데이터베이스의 소유자 명 (User Level EXPORT)[owner=user]

  - tables : EXPORT될 테이블의 리스트(Table Level EXPORT) [tables=(table1, table2, ...)]

  - log : EXPORT 실행 과정을 지정된 로그 파일에 저장

 

1. Full Level EXPORT

  전체 데이터베이스가 엑스포트 된다. 모든 테이블스페이스, 모든 사용자, 또한 모든 객체, 데이터들이 포함 된다.

  exp  userid=system/manager file='C:\full.dmp' full=y

    

2. User Level EXPORT

  사용자 자신이 만든 모든 오브젝트를 그 user가 EXPORT하는 방법

  exp userid=scott/tiger  file='C:\scott.dmp'

  혹시 비밀번호 특수문자 있을시 :   exp userid='scott'/'tiger!^'  file='C:\scott.dmp'

 

  SYSTEM계정으로 특정 user소유의 오브젝트들을 EXPORT 하는 방법

  exp userid=system/manager owner=scott  file='C:\scottuser.dmp' 

    

3. Table Level EXPORT

  SYSTEM계정으로 특정 유저의 table을 EXPORT하는 예제 

  다른 계정으로 EXPORT시 table의 user명까지 지정해야 EXPORT가 성공한다.

  exp userid=system/manager file='C:exp.dmp' tables=(scott.EMP, scott.DEPT)

 

  scott user로 table을 몇 개만EXPORT하는 예제

  자신의 table을 EXPORT할 때에는 user명을 지정할 필요가 없다. 

  exp userid=scott/tiger file='C:\exp.dmp' tables=(EMP, DEPT) log=exp.log

 

 

참고 : http://www.gurubee.net/lecture/1161

 

* IMPORT 옵션

 

  - userid : IMPORT를 실생시키는 계정의 username/password 명

  - file : IMPORT될 EXPORT 덤프 파일명

  - show : 파일 내용이 화면에 표시되어야 할 것인가를 나타냄(Y/N 플래그)

  - ignore : IMPORT중 CREATE명령을 실행할 때 만나게 되는 에러들을 무시할 것인지 결정(Y/N 플래그)

  - full : FULL엑스포트 덤프 파일이 IMPORT 할때 사용한다.

  - tables : IMPORT될 테이블 리스트

  - commit : 배열(배열의 크기는 BUFFER에 의해 설정됩니다) 단위로 COMMIT을 할것인가 결정 기본적으로는 테이블 단위로 COMMIT을 한다.

  - fromuser : EXPORT덤프 파일로 부터 읽혀져야 하는 객체들을 갖고 있는 테이터베이스 계정

  - touser : EXPORT덤프 안에 있는 객체들이 IMPORT될 데이터베이스 계정

 

1. IMPORT 예제

   

   전체 데이터베이스가 IMPORT(Full Level Export file을 Import)

   imp userid=system/manager file='C:\full.dmp'  full=y

 

   User Level Export file을 Import

   imp userid=scott/tiger file='C:\scott.dmp'

 

   User Level Export file을 다른 계정으로 IMPORT

   scott 유저의 데이터를 EXPORT받아 test 유저에게 IMPORT하는 예제 

   exp userid=system/manager file='C:\scott.dmp' owner=scott

   imp userid=system/manager file='C:\scott.dmp' fromuser=scott touser=test   

 

출처: https://godlvkhj.tistory.com/215 [하은양 믿음군 효실맘 호홍홍집s:티스토리]

1. Docker

우선 도커가 설치되어있어야 합니다.
도커 설치는 기존 포스팅을 참고 해주시기 바랍니다!

$ docker -v
Docker version 20.10.17, build 100c701

도커 버전을 확인합니다.
도커가 정상 설치되었으므로 본격적으로 테스트에 사용할 Alpine 리눅스를 설치해 보겠습니다.

docker pull alpine

이미지 부터 다운로드 해줍니다.

docker run -d -it --name alpine-container alpine:latest sh

그다음 테스트에 사용할 알파인 리눅스 컨테이너를 생성해줍니다.

2. Docker 컨테이너로 파일 복사하기

호스트에 있는 파일을 도커 컨테이너의 특정 경로로 복사하는 명령어는 다음과 같다.

docker cp <복사할 파일 경로> <컨테이너 이름>:<컨테이너 내부 파일 경로>

만일 호스트에 있는 test.txt 파일을 컨테이너의 /test 경로로 복사한다면, 다음과 같이 사용한다.

docker cp test.txt alpine-container:/test

만일 디렉토리를 옮기고 싶을때는 어떻게 해야할까?
디렉토리를 복사하는 경우도 별다른 옵션없이 복사가 가능하다.

3. Docker 컨테이너에서 파일 가져오기

그럼 반대로 도커 컨테이너에 있는 파일을 호스트로 가져오려면 어떻게 해야할까?
매우 단순하다. 파일 경로와 컨테이너의 경로를 바꿔주면 된다.

docker cp <컨테이너 이름>:<컨테이너 내부 파일 경로> <복사할 파일 경로> 

만일 도커 컨테이너의 test.txt 파일을 호스트의 경로인 /test 으로 복사를 원한다면, 다음과 같이 사용한다.

docker cp alpine-container:/test.txt /test/

역시 동일하게 디렉토리도 별다른 옵션없이 복사가 가능하다.

docker ps 
실행중이 도커 보기

docker ps -a
전체 도커 보기

docker rm [컨테이너 id]
도커에서 컨테이너 삭제 하기

docker images

docker rmi[이미지id]
도커 이미지 삭제 하기

docker rmi -f [이미지id]

 

일단 docker 라는 것을 처음 접하고, container를 실행해 본 후 가장 당황 스러웠던 것은 

 

"container에 어떻게 접속하지?"

 

였다. ( 필자는 그랬다.. ) 

 

일단 container 자체가 하나의 Proccess 였기에 container가 daemon으로 실행하고 나면 여기에 어떻게 접속해야할지 난감한 상황이..

 

root@~~# docker ps -a

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                  NAMES

c456623003b1        nimmis/apache       "/my_init"          40 seconds ago      Up 39 seconds       0.0.0.0:8080->80/tcp   high_ritchie  

[ Status가 Up 상태인거 보니 뭔가 실행 중이다. 접속은? ]

 

접속하는 방법은 매우 간단하다. 

docker exec 명령을 이용하면 된다.  docker exec 는 container에 특정 명령을 실행할 수 있는 것인데 이때 명령을 /bin/bash 라고 하면 된다. 

 

우리가 "접속" 하고 싶다는 의미는 해당 container 의 shell 에 접속하겠다는 의미이다. 

주의해야할 것은 docker exec 명령을 할때 옵션으로 -it 라고 덧붙여 주어야 한다. 이는 STDIN 표준 입출력을 열고 가상 tty (pseudo-TTY) 를 통해 접속하겠다는 의미이다. 

 

root@~~# docker exec -it  c456623003b1 /bin/bash

 

root@c456623003b1:~# 

 

위와 같이 hostname이 해당 container id로 바뀐 것을 볼 수 있다. 즉, container 내부에 접속한 상태라는 것이다. 

 

마지막으로 접속을 종료할때는 간단히 exit 명령을 통해 가능하다. 

 

출처: https://bluese05.tistory.com/21 [ㅍㅍㅋㄷ:티스토리]

0. 서론

데이터베이스전문가(SQLP) 자격증 취득을 위해 공부하던 중, mac M1에 오라클을 띄워서 실습 공부를 해야할 필요가 있었다. 그러나, 맥북 Apple Silicon(M1)에서는 로컬로 오라클을 띄울 수가 없다.

그래도, 아래 블로그를 찾아 Docker/Colima를 활용해서 Oracle을 띄울 수 있게 되었다.

참고 블로그

1. 설치

1.1 colima

docker desktop은 매우 무거운 프로그램이어서, 가벼운 CLI 환경에서 도커를 실행할 수 있는 오픈 소스인 colima를 사용한다.

brew install colima 

1.2 docker

docker desktop은 docker 공식 웹 홈페이지에서 설치할 수 있다.
Docker 웹 홈페이지

아니면, brew로 설치해도 된다.

# Docker Desktop
brew install --cask docker 

# Docker Engine
brew install docker

2. 실행

colima와 docker 모두 설치 후, colime를 x86_64 환경으로 띄워준다.

colima start --memory 4 --arch x86_64 

정상적으로 실행되고 있는지 확인하려면, docker ps나 images로 확인한다.

docker ps
docker images

모든 명령어를 포함해서 docker run
아직 미숙한 사람을 위해 컨테이너명까지 명시

# 컨테이너명 = oracle으로 명시
docker run --restart unless-stopped --name oracle -e ORACLE_PASSWORD=pass -p 1521:1521 -d gvenzl/oracle-xe 


#-d 를 넣으며 백단에서 계속 실행되...
docker run --name oracle -d -p 1521:1521 jaspeen/oracle-xe-11g

3. 활용

docker log 확인

# docker logs -f (컨테이너명)
docker logs -f oracle

4. DB접속

접속정보

  • host: localhost
  • database: xe (orcl로 되어 있으면 바꿔줘야 함)
  • port: 1521
  • user: system
  • password: XXXX

5. 샘플계정

도커에는 샘플계정이 포함되어 있지 않기 때문에 만들어줘야 한다.

# 한 줄씩 생성
docker exec -it oracle sqlplus
CREATE USER scott identified by tiger;
GRANT CONNECT, resource, dba to scott;

생성 후에는 아래 쿼리로 유저가 정상적으로 생성 된 것을 확인

select username from dba_users where username = 'SCOTT';

SCOTT 접속

docker exec -it oracle sqlplus

 

https://velog.io/@gpg/setting-docker-oracle

docker ps 
실행중이 도커 보기

docker ps -a
전체 도커 보기

docker rm [컨테이너 id]
도커에서 컨테이너 삭제 하기

docker images

docker rmi[이미지id]
도커 이미지 삭제 하기

docker rmi -f [이미지id]

 

$ docker search oracle-xe-11g
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
oracleinanutshell/oracle-xe-11g 206
wnameless/oracle-xe-11g-r2 Oracle Express Edition 11g Release 2 on Ubun… 79
orangehrm/oracle-xe-11g docker container with Oracle Express Editio… 16 [OK]
jaspeen/oracle-xe-11g Fork from sath89/docker-oracle-xe-11g - smal… 6 [OK]
christophesurmont/oracle-xe-11g Clone of the wnameless/oracle-xe-11g. 6
ukhomeofficedigital/oracle-xe-11g Oracle Database Express Edition 11g Container 4 [OK]
thebookpeople/oracle-xe-11g 3
wscherphof/oracle-xe-11g-r2 Oracle® Database Express Edition 11g Release… 3
acktsw/oracle-xe-11g fork from https://hub.docker.com/r/sath89/or… 2 [OK]
mcgregorandrew/oracle-xe-11g Oracle image with password expiry time set t… 2
alxfduch/oracle-xe-11g-tridion Oracle Express 11g R2 on Ubuntu 16.04 LTS Tr… 2
webdizz/oracle-xe-11g-sa This is a simple image based on sath89/oracl… 1 [OK]
dotcms/oracle-xe-11g 0
zeroturnaround/oracle-xe-11g 0
activeeon/oracle-xe-11g 0
nritholtz/oracle-xe-11g nritholtz/oracle-xe-11g 0
andyrbell/oracle-xe-11g-centos Oracle Express Edition 11g Release 2 on Cent… 0
gaesi/oracle-xe-11g Based on: oracleinanutshell/oracle-xe-11g 0
switchsoftware/oracle-xe-11g 0
aerisconsulting/oracle-xe-11g Oracle Express 11g R2 on Ubuntu 16.04 LTS (b… 0
paliari/oracle-xe-11g 0
jark/oracle-xe-11g-r2-cdc 0
avuletica/oracle-xe-11g-r2 Dockerfile of Oracle Database Express Editio… 0
larmic/oracle-xe-11g Using wnameless/oracle-xe-11g with created u… 0
toneloc01/oracle-xe-11g Out-of-the-box oralce xe image from ubuntu 1… 0
 

저 중에 하나를 docker pull 명령을 이용해서 받으면 된다.

$ docker pull jaspeen/oracle-xe-11g
Using default tag: latest
latest: Pulling from jaspeen/oracle-xe-11g
Image docker.io/jaspeen/oracle-xe-11g:latest uses outdated schema1 manifest format. Please upgrade to a schema2 image for better future compatibility. More information at https://docs.docker.com/registry/spec/deprecated-schema-v1/
863735b9fd15: Downloading [=====================> ] 27.89MB/65.67MB
4fbaa2f403df: Download complete
44be94a95984: Download complete
a3ed95caeb02: Download complete
05b9ddeb40d9: Download complete
b44894d2d2af: Download complete
1492d1fc5b9f: Download complete
c0f3c6ec8986: Waiting
fbfc89a21b1b: Waiting
740047056d21: Waiti
 

오라클 도커 컨테이너 실행

이제 받은 오라클 도커 이미지를 이용해서 오라클 인스턴스를 띄워보자.

$ docker run --name oracle -d -p 1521:1521 jaspeen/oracle-xe-11g
 

이제 sqlplus 를 실행해서 오라클 인스턴스에 붙어보자

$ docker exec -it oracle sqlplus
 
SQL*Plus: Release 11.2.0.2.0 Production on Fri Feb 25 12:44:34 2022
 
Copyright (c) 1982, 2011, Oracle. All rights reserved.
 
Enter user-name: system
Enter password:
 
Connected to:
Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production
 
SQL>

문제점

Bitbucket과는 달리 Github에는 기본적으로 100MB 이상의 파일을 올릴 수 없다.

Conditions for large files — User Documentation
https://help.github.com/articles/conditions-for-large-files/

그래서 100MB보다 큰 크기의 파일을 올리려고 시도하면 다음과 같은 경고 메시지를 보게 된다.

$ git push
Counting objects: 3086, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2980/2980), done.
Writing objects: 100% (3086/3086), 363.25 MiB | 935.00 KiB/s, done.
Total 3086 (delta 1236), reused 111 (delta 57)
remote: error: GH001: Large files detected. You may want to try Git Large File Storage — https://git-lfs.github.com.
remote: error: Trace: ***
remote: error: See http://git.io/iEPt8g for more information.
remote: error: File *** is 120.94 MB; this exceeds GitHub’s file size limit of 100.00 MB
To git@github.com:***
 ! [remote rejected] master -> master (pre-receive hook declined)
 ! [remote rejected] *** -> *** (pre-receive hook declined)
error: failed to push some refs to ‘git@github.com:***’

HEAD의 마지막 Commit에는 100MB가 넘는 파일이 없더라도 이전 Commit 중에 100MB 이상의 파일이 포함된 적이 있다면 이 경고를 피할 수 없다. 그러다 보니 게임과 같이 대용량의 Binary 파일을 자주 다루는 프로젝트를 Github에 올릴 때에는 높은 확률로 위 메시지를 만나게 된다.

해결책

다행히도 해결책이 존재한다. 경고 메시지에도 안내되어 있듯이 몇 가지 조처를 해주면 100MB 이상의 파일도 Github에 올릴 수 있다.

1. git-lfs 적용

Commit 과정에서 지정한 파일을 작게 조각내주는 Git extension인 git-lfs — Git Large File Storage https://git-lfs.github.com/ — 를 로컬에 설치한 뒤, 적용하려는 Repository 경로에서 다음 명령을 실행한다.

$ git lfs install
Updated pre-push hook.
Git LFS initialized.

그다음 용량이 큰 파일을 git-lfs의 관리 대상으로 등록해준다. 다음 예시는 120MB 정도의 exe 파일을 Stage에 추가한 상황에서, 확장자가 exe인 모든 파일을 git-lfs의 관리 대상으로 지정하고 Commit을 수행한 모습이다.

$ git lfs track “*.exe”
Tracking *.exe$ git commit -m “Large file included”
[master (root-commit) dd2b715] Large file included
(...)

이제 하단에 있는 3번 과정대로 Github에 push를 시도하면 된다. 그런데 기존에 100MB 이상의 파일을 Commit한 적이 있다면 여전히 100MB 이상의 파일을 올릴 수 없다는 경고 메시지를 보게 된다. 그럴 땐 다음 2번 과정을 적용해야 한다.

2. BFG Repo-Cleaner 적용

기존 Commit에서 100MB보다 큰 파일의 로그를 강제로 없애줘야 한다. BFG Repo-Cleaner — BFG Repo-Cleaner https://rtyley.github.io/bfg-repo-cleaner/ — 를 이용하면 그 작업을 손쉽게 적용할 수 있다.

공식 사이트에서 bfq-x.x.x.jar — x.x.x는 버전 — 를 받고, 대상이 되는 Repository에서 다음과 같이 그동안의 Commit에 포함된 100MB 이상의 파일을 정리하는 명령을 실행한다.

$ java -jar bfg-x.x.x.jar --strip-blobs-bigger-than 100M
(...)
Deleted files
    — — — — — — -
    Filename Git id
    — — — — — — — — — — — — — — 
    ***.exe | c304fcfb (120.9 MB)
(...)

간혹 다음과 같은 오류가 나타날 수 있다.

$ java -jar bfg-x.x.x.jar --strip-blobs-bigger-than 100MUsing repo : C:\***\.gitScanning packfile for large blobs: 132
Scanning packfile for large blobs completed in 13 ms.
Warning : no large blobs matching criteria found in packfiles — does the repo need to be packed?
Please specify tasks for The BFG :
bfg x.x.x
(...)

그럴 땐 아래 명령을 먼저 수행하고 다시 위의 bfg-x.x.x.jar에 의한 명령을 실행한다.

$ git repack && git gc
Counting objects: 3002, done.
(...)

3. git-push 재시도

위 과정들을 적용한 뒤 push를 시도하면 다음과 같이 성공 메시지를 볼 수 있다.

$ git push
Counting objects: 3089, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (1809/1809), done.
Writing objects: 100% (3089/3089), 234.95 MiB | 1.30 MiB/s, done.
Total 3089 (delta 1236), reused 2890 (delta 1229)
To git@github.com:***
 * [new branch] master -> master
 * [new branch] *** -> ***

마치며

개인적으로 Private repository를 무제한으로 만들 수 있다는 점 때문에 Github보다는 Bitbucket을 자주 사용한다. 이번에 큰 용량의 Binary 파일이 있는 프로젝트를 Github의 Private repository에 올릴 일이 있어서 용량 제한 정보를 찾아 봤는데 Github은 개별 파일당 100MB의 제한이 있고 Repository 전체에 대한 용량 제한은 없는 반면, Bitbucket은 개별 파일에 대한 용량 제한은 없지만 Repository당 2GB의 전체 용량 제한이 있다. 따라서 Private repository를 써야할 때 유료 결제가 부담이 안 되는 상황이라면 git-lfs 덕분에 Github을 쓰는 것이 더 효율적인 선택이라고 본다. 물론 전체 크기가 2GB가 넘는 프로젝트가 아니라면 Bitbucket으로 충분하다고 할 수 있겠다.

도움 주신 분

  • jjunCoder님의 제보로 오탈자를 수정했습니다. (2017. 2. 28)

참고자료

  1. Conditions for large files — User Documentation https://help.github.com/articles/conditions-for-large-files/
  2. Git Large File Storage https://git-lfs.github.com/
  3. Removing files from a repository’s history — User Documentation https://help.github.com/articles/removing-files-from-a-repository-s-history/
  4. BFG Repo-Cleaner https://rtyley.github.io/bfg-repo-cleaner/
  5. git repack vs git gc — aggressive http://git.661346.n2.nabble.com/git-repack-vs-git-gc-aggressive-td7564559.html
  6. What kind of limits do you have on repository/file/upload size? — Atlasssian Documentation https://confluence.atlassian.com/bitbucket/what-kind-of-limits-do-you-have-on-repository-file-upload-size-273877699.html

팀 프로젝트를 하다보면

 

기존에 github에서 로그아웃하고

 

다른 계정으로 작업하고 싶은 일이 생길 것입니다.

 

전 이 과정에서 많은 시간을 허비했으므로 (....) 기록을 남깁니다.

 

 

 

 

 

 

그 때 작업을 하는 방법을 포스트 하겠습니다.

 

 

 

 

github에 새 repository를 만들고

 

기존에 하던 방식대로 진행하다보면

 

 

$ git push -u origin master

 

를 하면

 

remote: Permission to newaccount/projectname.git denied to oldaccount.

fatal: unable to access 'https://github.com/newaccount/projectname.git/': The requested URL returned error: 403

newaccount는 현재 계정을

newproject는 현재 업로드하려고 만들어 놓은 repository의 이름을 의미합니다

 

이런 에러가 뜰 것이다.

 

 

 

 

 

 

먼저 git에 등록된 이름과

email을 확인해봅시다.

 

 

 

 

git config user.name

git config user.email

 

을 하면

 

구 계정과 구 이메일이 나올 것입니다.

 

 

 

 

 

 

 

 

git config --global user.name 신계정

git config --global user.email 신이메일

 

로 바꿔주고

 

다시한번 확인해봅시다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

그리고 제어판의 사용자 계정에 들어갑니다.

 

사용자 계정 -> 자격 증명 관리에 들어갑니다.

 

이 곳은 윈도우OS가 관리하는 인증 정보를 보관하는 곳입니다.

github 정보도 여기에서 관리합니다.

 

 

 

 

 

 

 

 

 

 

 

다음 Windows 자격 증명에 들어가면

 

일반 자격 증명 탭에

 

기존에 사용하던 토큰들이 있을 것입니다.

 

git 과 관련된건 지워줍시다.

 

 

 

 

 

 

그리고 업로드 하고 싶은 프로젝트 우클

 

git bash here 클릭

 

 

 

 

 

 

 

 

git init

 

git remote add origin 레파지토리 주소

 

git pull origin master

 

git add .

 

git commit -m "first commit"

 

git push -u origin master

(git add 띄고 . ) (git commit 띄고 -m) (git push 띄고 -u)

 

 

순서대로 합시다.

 

 

 

 

 

 

 

 

그러면 push 하려는 순간

 

새로 로그인 하라는 창이 뜰겁니다.

 

 

 

새 계정과 이메일로 바꿔주고

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

하면 잘 됩니다.

 

 

 

자신의 respository에 들어가보면 잘 된 모습을 확인할 수 있을 것입니다.

 

 

 

수고하셨습니다.

 

https://meaownworld.tistory.com/entry/github-git-bash-%ED%84%B0%EB%AF%B8%EB%84%90-%EA%B3%84%EC%A0%95-%EB%B3%80%EA%B2%BD

우선 Eclipse나 STS는 정적파일 같은 경우에는 바로 자동반영이 되지만 서버단 JAVA코드같은 경우에는 jLebel이라는 유료 플러그인을 사용하여야한다.

IntelliJ에서도 정적파일 같은 경우에는 설정을통해 자동으로 반영할 수 있다.

(서버단 코드는 반영되지 않음. 이클립스에서 java파일 저장시 서버가 새롭게 로드되는 불편한? 증상과 비슷한 원리로 로딩됨...)

 

1. gradle.build 파일을 연다.

 

2. dependencies에 아래 코드 를 추가한다.

dependencies {

	...
    
	developmentOnly('org.springframework.boot:spring-boot-devtools')
    //혹은
    //developmentOnly 'org.springframework.boot:spring-boot-devtools'
}

 

 

3. 상단 메뉴바 file탭의 Settings

 

 

4. Settings 창이 열리면

Settings - Bulid,Execution, Deployment - compiler를 찾아 들어가거나,

검색란에 Compiler를 검색한다.

Settings - Bulid,Execution, Deployment - compiler

 

4. 우측화면 체크박스

 

Automatically show first error in editor 체크 

Build Project automatically 체크

 

5. 좌측 메뉴탭 Advanced Settings 선택

 

6. 우측 화면에서 Allow auto-make to start even if developed application is currently running 체크

 

 

7. bulid,Execution, Deployment - Build Tools - Gradle선택 혹은 검색창에 Gradle 검색

 

8. Build and run using 과 Run test using 두개 모두 Gradle로 되어있다면 IntelliJ IDEA로 변경한다.

 

7~8번은 컴파일 실행시 빌드를 Gradle이 아닌 IntelliJ IDEA로 설정하는 것인데, 이것은 자동반영 효과 뿐만 아니라 전반적인 빌드 속도를 향상시켜준다. (분산방식으로 빌드를 해준다나 뭐라나 잘 모르겠다 좀 빨라지긴 한다.)

 

9. 모든 설정 완료후에 확인차 인텔리제이를 껐다 재실행 시키자.

Log에 restartedMain이라는 단어가 출력되면 정상적으로 세팅이 되었다는 뜻

<!DOCTYPE html>
<html>
<head>
    <title>오늘 날짜 입력</title>
</head>
<body>
    <input type="text" id="dateInput">
    <script>
        // 오늘 날짜 가져오기
        const today = new Date();
        const year = today.getFullYear();
        const month = String(today.getMonth() + 1).padStart(2, '0'); // 월은 0부터 시작하므로 1을 더하고 두 자리로 포맷팅
        const day = String(today.getDate()).padStart(2, '0'); // 일자를 두 자리로 포맷팅

        // yyyy-mm-dd 형식으로 날짜 문자열 생성
        const formattedDate = `${year}-${month}-${day}`;

        // input 요소에 날짜 설정
        document.getElementById('dateInput').value = formattedDate;
    </script>
</body>
</html>

HTML의 input 요소의 type 속성을 "text"로 설정하고 사용자가 오늘 날짜를 입력할 수 있도록 하려면 JavaScript를 사용하여 오늘 날짜를 가져와서 input 요소의 value 속성에 설정해야 합니다.

이 코드에서는 JavaScript를 사용하여 오늘 날짜를 가져오고, 이 날짜를 "yyyy-mm-dd" 형식의 문자열로 변환한 다음 input 요소의 value 속성에 설정합니다. 결과적으로 input 필드에 오늘 날짜가 표시됩니다.

JavaScript에서 typeof 연산자는 피연산자의 데이터 타입을 문자열로 반환합니다. typeof는 다음과 같은 데이터 타입을 반환할 수 있습니다:

"undefined": 값이 할당되지 않은 변수 또는 존재하지 않는 객체의 속성
"boolean": 참(true) 또는 거짓(false) 값을 가진 불리언 타입
"number": 숫자 타입 (정수 및 실수)
"string": 문자열 타입
"object": 객체, 배열, null 값
"function": 함수 타입
"symbol": 심볼 타입 (ES6부터 추가됨)
typeof 연산자 사용 예시:

console.log(typeof undefined); // "undefined"
console.log(typeof true); // "boolean"
console.log(typeof 42); // "number"
console.log(typeof "Hello, world!"); // "string"
console.log(typeof {}); // "object"
console.log(typeof []); // "object" (배열도 객체로 간주됩니다)
console.log(typeof null); // "object" (null도 객체로 간주됩니다)
console.log(typeof function() {}); // "function"
console.log(typeof Symbol()); // "symbol"
주의할 점은 typeof 연산자가 배열과 null을 "object"로 반환한다는 것입니다. 배열을 구분하려면 Array.isArray() 함수를 사용하면 됩니다.

위 폴더에서 특정 단어를 찾으려면 find 명령어를 사용할 수 있습니다. 다음은 Linux 및 macOS에서 하위 폴더에서 특정 단어를 찾는 방법입니다:

bashCopy code
find /시작경로 -type f -exec grep -l '특정단어' {}
  • /시작경로: 검색을 시작할 디렉토리 경로를 지정합니다. 이것은 검색을 시작할 디렉토리를 나타냅니다. 예를 들어, 전체 시스템에서 검색하려면 /를 사용하고, 현재 디렉토리에서 검색하려면 .를 사용할 수 있습니다.
  • -type f: find 명령을 사용하여 파일만 검색하도록 지정합니다. 디렉토리는 검색에서 제외됩니다.
  • -exec grep -l '특정단어' {} \;: 각 파일에 대해 grep 명령을 실행하여 특정 단어를 찾습니다. -l 옵션은 해당 파일에서 단어를 찾으면 파일의 이름을 출력하도록 지정합니다. {}는 find 명령에서 찾은 각 파일의 이름을 나타냅니다.

예를 들어, 현재 디렉토리부터 하위 폴더에서 "example"라는 단어를 찾으려면 다음 명령을 사용할 수 있습니다:

 
bashCopy code
find . -type f -exec grep -l 'example' {} \;

이 명령은 현재 디렉토리부터 시작하여 모든 하위 폴더에서 "example"라는 단어가 포함된 파일을 찾아 파일 경로를 출력합니다.

https://velog.io/@seokkitdo

1. 삼항조건 연산자(The Ternary Operator)

if .. else 조건문을 한 라인으로 줄여주는 유용한 기법입니다.

기존

const x = 20;
let answer;

if (x > 10) {
    answer = "greater than 10";
} else {
    answer =  "less than 10";
}

축약

const answer = x > 10 ? "greater than 10" : "less than 10";

또한 중첩해서 사용도 가능합니다.

const answer = x > 10 ? "greater than 10" : x < 5 ? "less than 5" : "between 5 and 10";

2. 간략계산법(Short-circuit Evaluation Shorthand)

기존 변수를 다른 변수에 할당하고 싶을 때, 기존 변수가 null, undefined 또는 비어있는 값이 아닌지 확인하고 싶을 수 있습니다. 이 경우에는 보통 긴 if 조건문을 사용하거나 간략계산법을 축약코딩을 사용할 수 있습니다.

기존

if (variable1 !== null || variable1 !== undefined || variable1 !== '') {
     let variable2 = variable1;
}

축약

const variable2 = variable1  || 'new';

믿기지 않으신다면 es6console에 아래 코드를 복사하여 확인해보세요!

let variable1;
let variable2 = variable1  || 'bar';
console.log(variable2 === 'bar'); // prints true

variable1 = 'foo';
variable2 = variable1  || 'bar';
console.log(variable2); // prints foo

variable1에 falsy한 값을 넣을 경우 bar 문자열이 variable2에 할당될 겁니다.

3. 변수 선언

함수를 시작하기 전 변수를 선언하는 것은 좋은 자세입니다. 이 축약기법은 동시에 여러개의 변수를 선언함으로써 시간과 코드라인을 줄일 수 있습니다.

기존

let x;
let y;
let z = 3;

축약

let x, y, z=3;

4. If Presence Shorthand

사소한 것일지 모르나, 알아두면 좋을 것 같습니다. if 조건문을 사용 시 때때로 대입 연산자를 생략할 수 있습니다.

기존

if (likeJavaScript === true)

축약

if (likeJavaScript)

위 두가지 예제가 정확하게 일치하지는 않습니다. 그 이유는 기존방법의 경우 true일 경우에만 조건문을 실행하는 반면 축약기법에서는 truthy 일 경우 통과하도록 되어있기 때문입니다.

다음 예제를 보시면 a가 true가 아닐 경우, 조건문을 실행합니다.
기존

let a;
if ( a !== true ) {
// do something...
}

축약

let a;
if ( !a ) {
// do something...
}

5. 반복문(For Loop)

만약 외부 라이브러리에 의존하지 않고 바닐라 자바스크립트만을 사용하길 원한다면 이 팁은 아주 유용합니다.

기존

const fruits = ['mango', 'peach', 'banana'];
for (let i = 0; i < fruits.length; i++)

축약

for (let fruit of fruits)

단순히 인덱스에만 접근하길 원한다면 다음과 같은 방법도 있습니다.

for (let index in fruits)

또한 리터럴 객체의 키에 접근하려는 경우에도 마찬가지로 동작합니다.

const obj = {continent: 'Africa', country: 'Kenya', city: 'Nairobi'}
for (let key in obj)
  console.log(key) // output: continent, country, city

Array.forEach를 통한 축약기법

function logArrayElements(element, index, array) {
  console.log("a[" + index + "] = " + element);
}
[2, 5, 9].forEach(logArrayElements);
// a[0] = 2
// a[1] = 5
// a[2] = 9

6. 간략 계산법(Short-circuit Evaluation)

기본 값을 할당하기 위해 파라미터가 null 또는 undefined인지 확인하느라 6줄의 코드를 작성하는 것보다 Short-circuit Evaluation 계산법을 이용해 한 줄의 코드로 작성하는 방법이 있습니다.

기존

let dbHost;
if (process.env.DB_HOST) {
  dbHost = process.env.DB_HOST;
} else {
  dbHost = 'localhost';
}

축약

const dbHost = process.env.DB_HOST || 'localhost';

process.env.DB_HOST 값이 있을 경우 이 값을 dbHost에 할당하지만 없다면 localhost를 할당합니다. 이에 대한 정보는 여기 에서 더 살펴볼 수 있습니다.

7. 십진법 지수(Decimal Base Exponents)

0이 많이 포함된 숫자를 적을 때, 이 수많은 0들을 제외하고 숫자를 작성한다면 있어보이지 않을까요? 예를들어 1e7은 10,000,000 과 동일하며 1뒤에 7개의 0이 딸려온 것을 의미합니다.

기존

for (let i = 0; i < 10000; i++) {}

축약

for (let i = 0; i < 1e7; i++) {}

// All the below will evaluate to true
1e0 === 1;
1e1 === 10;
1e2 === 100;
1e3 === 1000;
1e4 === 10000;
1e5 === 100000;

8. 객체 프로퍼티

자바스크립트에서 객체 리터럴 표기법은 코딩을 더 쉽게 만듭니다. ES6 는 객체에 프로퍼티를 할당하는것을 더 쉽게 만들어 주었습니다. 만약 객체의 프로퍼티 이름이 키 이름과 같을 경우에 축약기법을 사용할 수 있습니다.

기존

const x = 1920, y = 1080;
const obj = { x:x, y:y };

축약

const obj = { x, y };

9. 화살표(Arrow) 함수

기존의 함수는 읽고 쓰기가 쉽도록 되어있지만 다른 함수 호출에 중첩하여 사용하게 될 경우 다소 길어지고 헷갈리게 됩니다.
기존

function sayHello(name) {
  console.log('Hello', name);
}

setTimeout(function() {
  console.log('Loaded')
}, 2000);

list.forEach(function(item) {
  console.log(item);
});

축약

sayHello = name => console.log('Hello', name);

setTimeout(() => console.log('Loaded'), 2000);

list.forEach(item => console.log(item));

화살표 함수와 일반 함수의 차이점은 그 생김새로도 확인할 수 있지만 this가 기존 함와 다르게 작동합니다. 따라서 위 두 예제는 완전히 동일한 예제는 아닙니다.
이 차이점에 대해 알고 싶으시다면 여기 를 클릭해주세요.

10. 묵시적 반환(Implicit Return)

return 은 함수 결과를 반환하는데 사용되는 키워드입니다. 한 줄로만 작성된 화살표 함수에서는 return 명령어가 없어도 자동으로 반환하도록 되어있습니다. 다만 중괄호를 생략해야지만 return 키워드도 생략할 수 있습니다.

여러 라인의 코드(객체 리터럴) 를 반환하려면 중괄호 대신 () 를 사용해서 함수를 감싸면 됩니다. 다음과 같이 한 줄로 코드가 작성되었음을 확인할 수 있습니다.

기존

function calcCircumference(diameter) {
  return Math.PI * diameter
}

축약

calcCircumference = diameter => (
  Math.PI * diameter;
)

11. 파라미터 기본값 지정

기존에는 if 문을 통해 함수의 파라미터에 기본 값을 지정해주었지만 ES6 에서는 함수 선언문 자체에 파라미터의 기본 값을 설정해 줄 수 있습니다.
기존

function volume(l, w, h) {
  if (w === undefined)
    w = 3;
  if (h === undefined)
    h = 4;
  return l * w * h;
}

축약

volume = (l, w = 3, h = 4 ) => (l * w * h);

volume(2) //output: 24

12. 템플릿 리터럴(Template Literals)

문자열로 ' + '를 입력하며 자바스크립트 값을 추가하는 손가락 노동에 지치지 않으시나요? 더 쉬운 방법은 없을까요? 만약 ES6 사용하실 줄 안다면 백틱(backtick)을 사용해서 스트링을 감싸고 변수를 ${}로 감싸서 손쉽게 연결 할 수 있습니다.

기존

const welcome = 'You have logged in as ' + first + ' ' + last + '.'

const db = 'http://' + host + ':' + port + '/' + database;

축약

const welcome = `You have logged in as ${first} ${last}`;

const db = `http://${host}:${port}/${database}`;

13. 구조 분해 할당(Destructuring Assignment)

유명한 프레임워크로 개발을 하고 있다면 컴포넌트나 API간 데이터를 전송하기 위해서 객체 리터럴이나 배열로 이뤄진 데이터를 사용하게 될 가능성이 큽니다. 데이터 객체가 컴포넌트에 들어가게 되면, unpack이 필요합니다.

기존

const observable = require('mobx/observable');
const action = require('mobx/action');
const runInAction = require('mobx/runInAction');

const store = this.props.store;
const form = this.props.form;
const loading = this.props.loading;
const errors = this.props.errors;
const entity = this.props.entity;

축약

import { observable, action, runInAction } from 'mobx';

const { store, form, loading, errors, entity } = this.props;

추가적으로 커스텀 변수명을 지정할 수 있습니다.

const { store, form, loading, errors, entity:contact } = this.props;

위의 코드는 구조분해할당을 통해 entity를 가져오고 난 후 contact라는 변수명을 지정한 겁니다.

14. 여러줄로 문자열 쓰기

한 줄 이상의 문자열을 입력하게 될 경우가 있다면 기존의 방법보다 훨씬 편한 방법이 있습니다.

기존

const lorem = 'Lorem ipsum dolor sit amet, consectetur\n\t'
    + 'adipisicing elit, sed do eiusmod tempor incididunt\n\t'
    + 'ut labore et dolore magna aliqua. Ut enim ad minim\n\t'
    + 'veniam, quis nostrud exercitation ullamco laboris\n\t'
    + 'nisi ut aliquip ex ea commodo consequat. Duis aute\n\t'
    + 'irure dolor in reprehenderit in voluptate velit esse.\n\t'

아래와 같은 방법으로 백틱(backtick)을 사용하는 방법도 있습니다.

축약

const lorem = `Lorem ipsum dolor sit amet, consectetur
    adipisicing elit, sed do eiusmod tempor incididunt
    ut labore et dolore magna aliqua. Ut enim ad minim
    veniam, quis nostrud exercitation ullamco laboris
    nisi ut aliquip ex ea commodo consequat. Duis aute
    irure dolor in reprehenderit in voluptate velit esse.`

15. 전개 연산자(Spread operator)

ES6 에서 소개된 스프레드 연산자는 자바스크립트 코드를 더 효율적이고 재미있게 사용할 수 있는 방법들을 제시합니다. 간단히는 배열의 값을 변환하는데 사용할 수 있습니다. 스프레드 연산자를 사용하는 방법은 점 세개(...)를 붙이면 됩니다.

기존

// joining arrays
const odd = [1, 3, 5];
const nums = [2 ,4 , 6].concat(odd);

// cloning arrays
const arr = [1, 2, 3, 4];
const arr2 = arr.slice()

축약

// joining arrays
const odd = [1, 3, 5 ];
const nums = [2 ,4 , 6, ...odd];
console.log(nums); // [ 2, 4, 6, 1, 3, 5 ]

// cloning arrays
const arr = [1, 2, 3, 4];
const arr2 = [...arr];

concat() 함수와는 다르게 스프레드 연산자는 배열 내부의 원하는 위치 어디에나 추가할 수 있습니다.

const odd = [1, 3, 5 ];
const nums = [2, ...odd, 4 , 6];

스프레드 연산자는 또한 ES6 의 구조화 대입법(destructuring notation)와 함게 사용할 수도 있습니다.

const { a, b, ...z } = { a: 1, b: 2, c: 3, d: 4 };
console.log(a) // 1
console.log(b) // 2
console.log(z) // { c: 3, d: 4 }

16. 필수(기본) 파라미터(Mandatory Parameter)

기본적으로 자바스크립트 함수의 파라미터값을 받지 않았을 경우, undifined로 지정합니다. 몇몇 다른 언어들은 에러 메시지를 나타내기도 합니다. 이런 기본 파라미터는 강제로 할당하는 방법은 if문을 사용해서 undefined일 경우 에러가 나도록 하거나,
Mandatory Parameter를 사용하는 방법이 있습니다.

기존

function foo(bar) {
  if(bar === undefined) {
    throw new Error('Missing parameter!');
  }
  return bar;
}

축약

mandatory = () => {
  throw new Error('Missing parameter!');
}

foo = (bar = mandatory()) => {
  return bar;
}

17. Array.find

자바스크립트로 특정 값을 찾기 위한 함수를 작서앟다보면 보통 for 루프를 이용해서 접근을 하곤 합니다. 그러나 ES6 에서 find()라는 새로운 함수가 생겼습니다.

기존

const pets = [
  { type: 'Dog', name: 'Max'},
  { type: 'Cat', name: 'Karl'},
  { type: 'Dog', name: 'Tommy'},
]

function findDog(name) {
  for(let i = 0; i<pets.length; ++i) {
    if(pets[i].type === 'Dog' && pets[i].name === name) {
      return pets[i];
    }
  }
}

축약

pet = pets.find(pet => pet.type ==='Dog' && pet.name === 'Tommy');
console.log(pet); // { type: 'Dog', name: 'Tommy' }

18. Object [key]

Foo.bar를 Foo['bar'] 로 적을 수 있다는 걸 알고 있으신가요? 왜 후자와 같이 코딩을 해야 하는지 의문이 들 수도 있지만, 재사용이 용이한 코드 블락을 작성하기 위해서는 매우 효율적인 방법입니다.
아래의 간단한 validation 함수를 확인해보세요

기존

function validate(values) {
  if(!values.first)
    return false;
  if(!values.last)
    return false;
  return true;
}

console.log(validate({first:'Bruce',last:'Wayne'})); // true

위 함수로 validation 기능을 완벽하게 사용할 수 있습니다. 하지만 form 요소들과 validation 옵션으로 사용해야하는 영역과 규칙이 많을 경우 위 함수는 점점 복잡해지고 길어지게 됩니다. 이를 방지하기 위해서 실행시 옵션을 부과할 수 있는 포괄적인 validation함수를 작성하는 방법을 알아보겠습니다.

축약

// object validation rules
const schema = {
  first: {
    required:true
  },
  last: {
    required:true
  }
}

// universal validation function
const validate = (schema, values) => {
  for(field in schema) {
    if(schema[field].required) {
      if(!values[field]) {
        return false;
      }
    }
  }
  return true;
}


console.log(validate(schema, {first:'Bruce'})); // false
console.log(validate(schema, {first:'Bruce',last:'Wayne'})); // true

위의 예시처럼 사용한다면 모든 form에 공통으로 적용할 수 있는 validation 함수를 작용할 수 있습니다.

19. 단항 비트 논리부정 연산자 (Double Bitwise NOT)

비트 연산자는 자바스크립트를 처음 배울 때를 제외하고는 한 번도 적용해 본 적 없는 연산자일 겁니다. 애당초 이진법으로 코딩하지 않는다면 1과 0을 사용 할 일이 없죠.

하지만 단항 비트 논리부정 연산자를 효율적으로 사용할 수 있는 방법을 알려드립니다.
바로 Math.floor() 함수의 대체용으로 사용할 수 있다는 것이죠. 또 Math.floor()함수보다 훨씬 빠르게 작동한다는 장점도 있습니다. 단항 비트 논리부정 연산자에 대해서는 여기 에서 더 알아보실 수 있습니다.

기존

Math.floor(4.9) === 4  //true

축약

~~4.9 === 4  //true

20. 거듭제곱(Exponent Power)

Math.pow를 통해 거듭제곱을 나타낼 수 있습니다.

기존

Math.pow(2,3); // 8
Math.pow(2,2); // 4
Math.pow(4,3); // 64

축약

2**3 // 8
2**4 // 4
4**3 // 64

21. 문자열 숫자 변환

때때로 필요한 데이터는 숫자 형식이어야 하는데 문자열 타입이 오는 경우가 있을 수 있습니다. 하지만 이러한 타입변환은 손쉽게 해결할 수 있습니다.

기존

const num1 = parseInt("100");
const num2 =  parseFloat("100.01");

축약

const num1 = +"100"; // converts to int data type
const num2 =  +"100.01"; // converts to float data type

'study > Front' 카테고리의 다른 글

java script 오늘날짜 입력  (0) 2023.10.25
Javascript typeof. typeof undefined  (0) 2023.10.25
find  (0) 2023.10.24
formData axios selectbox  (0) 2023.10.16
axios.js  (0) 2023.10.16
<select id="flag">
    <option value="1">선택</option>
</select>

특정 value 값의 선택을 true시킴
$("#flag").val("1").prop("selected", true);

전체 삭제 Just..
$("select#아이디 option").remove();


$('#selectBox option:selected').val();            // select box 선택 값 출력
$('#selectBox option:selected').text();           // option 값 출력


특정 옵션 삭제
$("select#아이디 option[value='option']").remove();


옵션 추가
$("select#아이디").append("<option value='option'>옵션추가</option>");


<input type="checkbox" name="ckall">
<input type="checkbox" name="ck">
<input type="checkbox" name="ck">

$("input[name=ckall]").click(function(){
    if($("input[name=ckall]").prop("checked")){
        $("input[name=ck]").prop("checked",true);   // 전체 체크
    }else{
        $("input[name=ck]").prop("checked",false);  // 전체 체크 해제
    }
});

var chkArray = new Array();

$("input:checkbox[name='delCheck']:checked").each(function(){
    chkArray.push($(this).closest('tr').data('revolutionSeq'));
});

 

인텔리제이 코드정렬

command + option + shift + L 누른후 ok

 

var formData = new FormData(); // Currently empty
formData.append(name, value);
formData.append(name, value, filename);
생성자
FormData()
새로운 FormData 객체를 생성합니다.

메소드
FormData.append()
FormData 객체안에 이미 키가 존재하면 그 키에 새로운 값을 추가하고, 키가 없으면 추가합니다.

FormData.delete()
FormData 객체로부터 키/밸류 쌍을 삭제합니다.

FormData.entries()
이 객체에 담긴 모든 키/밸류 쌍을 순회할 수 있는 iterator를 반환합니다.

FormData.get()
FormData 객체 내의 값들 중 주어진 키와 연관된 첫번째 값을 반환합니다.

FormData.getAll()
FormData 객체 내의 값들 중 주어진 키와 연관된 모든 값이 담긴 배열을 반환합니다.

FormData.has()
FormData 객체에 특정 키가 포함되어 있는지 여부를 나타내는 boolean 을 반환합니다.

FormData.keys()
이 객체에 담긴 모든 키/벨류 쌍들의 모든 키들을 순회 할 수 있는 iterator를 반환합니다.

FormData.set()
FormData 객체 내에 있는 기존 키에 새 값을 설정하거나, 존재하지 않을 경우 키/밸류 쌍을 추가합니다.

FormData.values()
이 객체에 포함된 모든 밸류를 통과하는 iterator를 반환합니다.


let formData = new FormData();

// ... formData.append("키이름", "값"); 생략

for (let key of formData.keys()) {
	console.log(key, ":", formData.get(key));
}

for(let value of formData.values()){
	console.log(value, " : ",formData.get(value));
}

 

<template>
  <div>
    <form v-on:submit.prevent="submitForm"> 
      <div>
        <label for="username">id:</label>
        <input id="username" type="text" v-model="username">
      </div>
      <div>
        <label for="password">pw:</label>
        <input id="password" type="password" v-model="password">
      </div>
      <button type="submit">login</button>
    </form>
  </div>
</template>

<script>
import axios from 'axios'

export default {
  data: function(){
    return {
      username: '',
      password: '',
    }
  },
  methods: {
    submitForm: function() {
      
      console.log(this.username, this.password);
      var url = 'https://jsonplaceholder.typicode.com/users';
      var data = {
        username: this.username,
        password: this.password
      }
      axios.post(url, data)
        .then(function(response){
        console.log(response);
       })
        .catch(function(error){
          console.log(error);
        });
    }
  }
}
</script>

엑시오스(axios)

'study > Front' 카테고리의 다른 글

java script 오늘날짜 입력  (0) 2023.10.25
Javascript typeof. typeof undefined  (0) 2023.10.25
find  (0) 2023.10.24
모르면 손해보는 21가지 JavaScript 축약코딩 기법seokkitdo·2021년 12월 31일  (0) 2023.10.20
axios.js  (0) 2023.10.16
let tmpDt = "empty";
    fn_get_corp_cd=function(){
    axios({
       method: 'get', //통신 방식
       url: '/logs/getCORP_CD', //통신할 페이지
       data: {} //인자로 보낼 데이터
    })
    .then(response=>{
       console.log('console responseData',response);
       tmpDt = response;
       return tmpDt;
    })
    .catch(error=>{
       //console.log(error);
       tmpDt = error;
       return tmpDt;

    })


    $("#corpcdselect").empty();
    $("#corpcdselect").append(htm)


}
function resolveAfter2Seconds() {
    return new Promise((resolve) => {
       setTimeout(() => {
          resolve(tmpDt);
       }, 2000);
    });
}

async function asyncCall() {
    console.log('calling');
    const result = await axios.post('/logs/getCORP_CD').then(response=>{
          console.log(response)
          });

    console.log("result2 ",result);
    const result2 = await axios.post('/logs/getCORP_CD').then(res=>{
       console.log(res)
    });

    // Expected output: "resolved"
}

 TestApiCall = async function(){
    try {
       const response = await axios.post('/logs/getCORP_CD')
       console.log("response >>", response.data)
       const response2 = await axios.post('/logs/getCORP_CD')
       console.log("response >>", response2.data)
    } catch(err) {
       console.log("Error >>", err);
    }
}

 

 

axios.js
0.06MB

다른 테이블의 SELECT 한 결과물을 참조하여 현재 테이블을 UPDATE 해야 하는 상황이 있습니다.

Oracle, MS-SQL, Maria DB, Mysql DBMS마다 다른 문법을 지원하므로 방법도 각자 다릅니다.

저는 Oracle을 사용 중인데 다른 DBMS 보다 조금 문법이 어려운 것 같네요 이 방법에 한해서는요 ㅎㅎ

table master, table user 두 개의 테이블이 있다고 가정하겠습니다.

master 테이블의 Status을 user 테이블의 Status의 값을 참조하여 변경하는 쿼리를 작성해보겠습니다.

MS-SQL

UPDATE
    master
SET
    master.status = cust.status
FROM
    master m
INNER JOIN
    cust c
ON 
    m.id = c.id;

MySQL and MariaDB

UPDATE
    master m,
    cust c
SET
    m.status = c.status
WHERE
    m.id = c.id;

Oracle

단순 업데이트만 사용하는 방법

EXISTS를 사용하지 않으면 첫 번째 서브 쿼리의 id 키 값이 매칭 되지 않는 값들은 master의 status 값이 null로 변경됩니다. 

UPDATE master m
SET    m.status = (select c.status
                  from cust c 
                  where m.id = c.id)
WHERE EXISTS (select 1
              from cust c
              where m.id = c.id)

merge into를 구문을 사용하는 방법

MERGE INTO master m
USING cust c
ON (m.id = c.id)
WHEN MATCHED THEN UPDATE SET m.status = c.status;

Oracle 실습

master table
cust table

이 쿼리를 실행해보겠습니다.

UPDATE master m
SET    m.status = (select c.status
                  from cust c 
                  where m.id = c.id)
WHERE EXISTS (select 1
              from cust c
              where m.id = c.id)

우리가 기대하는 결과는 master 테이블의 test1, test3, test5의 status값이 FALSE로 변환되는 결과입니다.

update 결과

올바르게 변환됩니다!

그런데 만약 아래와 같이 EXIST 구문을 빼면 어떻게 될까요?

UPDATE master m
SET    m.status = (select c.status
                  from cust c 
                  where m.id = c.id)
update 결과

test2와 test4는 id 키 값이 매칭되는 것이 없으므로 status의 값이 null로 변경됩니다. 기본적으로 update 구문에서 where 조건을 설정하지 않으면 전체 테이블을 update하게 되어서 나타나는 결과입니다.

merger into 쿼리의 결과 역시 제대로 업데이트 되는 것을 볼 수 있습니다!

MERGE INTO master m
USING cust c
ON (m.id = c.id)
WHEN MATCHED THEN UPDATE SET m.status = c.status;
merge into 결과
좋아요8
공유하기
게시글 관리

 

오라클에서 테이블의 값을 수정하기 위해서는 UPDATE 문을 사용하면 된다. WHERE 절에 해당하는 행이 여러 개인 경우 한 번에 여러 건의 행이 SET 절의 값으로 변경된다. UPDATE 문은 시스템 운영 시 자주 사용하므로 다양한 방법을 익혀두는 것이 좋다.

 

오라클 UPDATE 문

기본 사용법

UPDATE emp
   SET job = 'MANAGER'
     , deptno = 20
 WHERE empno = 7566

 

위는 WHERE 절의 empno에 해당하는 데이터의 job, deptno 컬럼을 수정하는 쿼리이다.  empno는 PK이기 때문에 한건의 데이터만 수정되지만 PK가 아니고 여러 건이 조회되면 여러 건의 데이터가 한 번에 수정된다.

 

SET 절에 서브쿼리 사용법

UPDATE emp a
   SET a.sal  = (SELECT MAX(aa.sal) FROM emp aa WHERE aa.job = a.job)
     , a.comm = (SELECT MAX(aa.comm) FROM emp aa WHERE aa.job = a.job)
 WHERE a.job = 'ANALYST'

 

다른 테이블에서 데이터를 조회하여 UPDATE를 해야 하는 경우가 있다. 조인을 사용하면 편리할 거 같지만 UPDATE문에서 조인은 제약사항이 많기 때문에 서브 쿼리를 활용하면 쉽게 해결되는 경우가 많다.

 

UPDATE emp a
   SET (a.sal, a.comm)  = (SELECT MAX(aa.sal), MAX(aa.comm)
                             FROM emp aa 
                            WHERE aa.job = a.job)
 WHERE a.job = 'ANALYST'

 

동일한 서브쿼리를 반복해서 사용해야 하는 경우 컬럼을 묶어서 한 번의 서브 쿼리로 업데이트 값을 입력할 수 있다.

 

WHERE 절에 IN 사용법

UPDATE emp
   SET sal = 3000
 WHERE empno IN (SELECT aa.empno
                   FROM emp aa
                      , dept bb
                  WHERE aa.job = 'ANALYST'
                    and aa.deptno = bb.deptno)

 

업데이트 범위를 정할 때 다른 테이블을 참조해야 할 경우 IN을 사용하여 데이터를 포함시키거나 NOT IN을 사용하여 제외시킬 수 있다.

 

UPDATE emp
   SET comm = 500
 WHERE (job, deptno) IN (SELECT aa.job, aa.deptno
                           FROM emp aa
                          WHERE aa.deptno = 30)

 

IN의 서브 쿼리에서 키값이 여러 개인 경우 컬럼을 묶어서 조건을 맵핑할 수 있다.

 

UPDATE emp a
   SET a.comm = 500
 WHERE a.ROWID IN (SELECT aa.ROWID
                     FROM emp aa
                    WHERE aa.deptno = 30)

 

동일한 데이블을 사용한다면 ROWID를 키로 사용할 수 있다.

 

WHERE 절에 EXISTS 사용법

UPDATE emp a
   SET a.comm = 500
 WHERE EXISTS (SELECT 1
                 FROM emp aa
                WHERE aa.empno = a.empno
                  AND aa.deptno = 30)

 

EXISTS를 사용하면 쉽게 데이터를 제외시키거나 포함시킬 수 있다. IN과 비슷한 역할을 하지만 성능면에서 조금 더 나을 수 있다.

 

UPDATE emp a
   SET a.comm = 500
 WHERE NOT EXISTS (SELECT 1
                     FROM emp aa
                    WHERE aa.empno = a.empno
                      AND aa.deptno = 30)

 

조인하여 UPDATE 하는 방법

UPDATE (SELECT a.sal
             , a.comm
             , b.sal AS sal_2
             , b.comm AS comm_2
          FROM emp a
             , emp_man b
         WHERE a.empno = b.empno)
   SET sal = sal_2 
     , comm = comm_2

 

두 개의 테이블을 조인해서 업데이트해야 할 경우 사용하는 방법이다. 위의 쿼리는 오라클 11.2 이하의 버전에서는 오류가 발생하며 아래의 MERGE 문을 사용해야 한다.

 

MERGE 
 INTO emp a
USING emp_man b
   ON (a.empno = b.empno)
 WHEN MATCHED THEN
      UPDATE 
         SET a.sal = b.sal
           , a.comm = b.comm;

 

MERGE 문은 오라클 9i부터 사용할 수 있다. BYPASS_UJVC 힌트를 사용하는 방법이 있으나 11g부터 공식적으로 사용을 중단하였기 때문에 특별한 경우가 아니면 MERGE 문을 사용할 것을 권장한다.

 

 

 

 

MERGE INTO를 하는 중 다음과 같은 오류 메세지가 발생하였다.

 

 

ORA-30926 : 원본 테이블의 고정 행 집합을 가져올 수 없습니다.

 

ORA-30926 : unable to get a stable set of rows in the source tables

 

 

MERGE INTO 구문

 

====================================================================

MERGE INTO table_name alias         -- (실제 데이터를 INSERT 또는 UPDATE할 테이블)

USING (table | view | subquery) alias 

-- 실제 데이터를 조회할 대상 테이블(뷰, 서브쿼리)

-- 대상 테이블이 없는 경우 DUAL 테이블 사용

ON (join condition) 

-- 조건절 (Where절) 이 조건에 의해 아래 MATCHED / NOT MATCHED 로 분기

WHEN MATCHED THEN  -- ON 조건에 해당하는 데이터(레코드)가 존재한다면 

UPDATE SET [column1] = [value1] -- 해당 레코드를 대상으로 UPDATE 실행

WHEN NOT MATCHED THEN -- ON 이하의 조건에 해당하는 데이터(레코드)가 존재하지 않는다면

INSERT (column1, column2 ...) VALUES (value1, value2 ...); 

-- 새 데이터를 추가해야 하므로 INSERT 실행

====================================================================

 

MERGE INTO 구문 중 INSERT와 UPDATE는 하나의 레코드를 대상으로 작업을 수행할 수 있는데, 2개 이상의 SELECT 결과가 나와서 발생하는 오류였다.

 

USING ( ) ON ( ) 조건에 의해 SELECT 된 결과가 2건이상 발생하는 경우 동일한 레코드가 중복 삽입되는 될 수 있으며, 이때 PK 무결성 오류가 발생하기도 한다.

또는 update되는 로우가 1건 이상이 발생한다.

 

 

정리하자면

 

1) INTO 절에 사용되는 테이블에 Primary Key 를 사용하는 경우    

 - 즉 INSERT 구문에서 DUPLICATE가 발생하거나 UPDATE 에 MULTI ROW가 UPDATE되는 경우

2) ON 구문에서 UPDATE되는 ROW가 1개 이상일 경우

 - 즉 ON 구문에서 맞는 테이블 값이  하나 이상일 경우

 

 

그렇기 때문에 USING ( ) 조건에 의해 산출되는 SELECT 결과가 중복 레코드를 갖지 않도록 수정하면 해결된다.

 

ORACLE 12 이상

MERGE INTO SPEC_MANUF m

USING COMM_CD c

ON (m.CORP_CD = c.SCODE AND c.MCODE='11' )

WHEN MATCHED THEN UPDATE SET m.CORP_NATION = c.CALL_NAME;

 

SELECT * FROM SPEC_MANUF;

 

 

ORACLE 12 이하

UPDATE SPEC_MANUF a

SET a.CORP_NATION = (SELECT aa.CALL_NAME

FROM COMM_CD aa

WHERE aa.SCODE = a.CORP_CD

AND aa.MCODE = '11')

WHERE EXISTS (SELECT aa.SCODE

FROM COMM_CD aa

WHERE aa.SCODE = a.CORP_CD

AND aa.MCODE = '11'

);

 

 

1. JavaScript Input

1) e.target.files[0]

: 해당 인풋에서 발생한 이벤트를 통해 업로드 된 파일을 찾을 수 있다.

<input type='file' onChange={(e) => {onFileUpload(e)} />
  • input이 발생하는 태그에서 onChange로 그 이벤트를 통해 다룰 수 있다.
  function onFileUpload(event) {
    event.preventDefault();
    let file = event.target.files[0];
  }
  • e.target.files에 해당 파일이 담겨있는 것을 볼 수 있다.
  • 만약 multipl 속성이 있는 태그라면 [0] 번 이외에 인덱스 값을 추가로 가지는 것을 알 수 있다.
    (multipl 속성을 가질 경우 한번에 여러개 파일을 드레그 해서 첨부할 수 있음)
  • 만약 1개씩 여러개의 파일을 받고 싶다면. (이건 다음 화에서...)

2) formData

: 파일은 아무 형태로나 서버로 전달할 수 없다. 위에서 받아온 file도 그 형태 그대로 전달 할 수 없다.
때문에 formData 라는 것을 사용하게 된다.

let formData = new FormData();

(1) FormData.append()

: 가장 많이 쓰게 될 formdata의 내장 함수로 form 데이터 안에 키,값의 형태로 담을때 사용된다.

  const upload = () => {
    let formData = new FormData();
    formData.append('files', files);
  }
  • 서버에서 files 라는 키에 파일을 담아주기를 원한다면 다음과 같은 방법으로 해당 값을 append를 이용해서 담도록 한다.
    (Tip:

(2) Json과 file

: file과 text 동시에 보내기

  const upload = () => {
    const temp = JSON.stringify({
      name: selectedClassName,
      price: selectedPrice,
      sale: selectedDiscount / 100,});
    let formData = new FormData();
    formData.append('body', temp);
    formData.append('files', files);
  }
  • 파일과 그 해당 파일과 관련된 text를 모두 보내는 경우 동일한 딕셔너리(객체)에 담아 보낼 수 없다.
  • 따라서 각각의 append를 이용해서 서버로 전송하도록 한다.
  • 위의 경우 formData의 body 라는 키에 stringify를 이용해서 json 데이터를 분리해서 담았다.

(3) file 여러개 담기

: file은 리스트(배열)의 형태로 만들어 formData에 바로 담을 수 없다.
(이 경우 빈 객체만 나오는 것을 알 수 있다)

  const upload = () => {
    let formData = new FormData();
    for (let i = 0; i < files.length; i++) {
        formData.append('files', files[i]);
    }
  }
  • 따라서 배열에 담아온 파일들을 한번 더 반복문을 사용해서 append에 하나 하나 담아야 한다.

'study' 카테고리의 다른 글

문제점: Tomcat 버전 정보 노출  (0) 2025.05.09
Mac 내장 Apache로 웹서버 운영하기  (0) 2025.01.23
SVN CLEANUP 옵션설명  (0) 2023.04.14
jar 압축 / 해제  (0) 2023.03.09
php db접속  (0) 2023.03.09

+ Recent posts