Translate

2017년 10월 27일 금요일

[IntelliJ] CSV 파일을 테이블 형식으로 보면서 편집하자.






CSV 파일 사용할일이 종종 있는데,,

IntelliJ 에서 편하게 볼 수 있는 방법이 없을까 하고 알아보니 기본 기능에 포함이 되어 있었다.
아래와 같이 사용이 가능하다.



1. CSV 파일을 열면 기본적으로 아래와 같이 보여지게 된다.
1004lucifer



2. Edit > Edit as Table 선택




3. 아래와 같이 설정화면이 나오는데 그냥 OK를 누르던가 설정을 변경하면 된다.
  (나의 경우엔 세미콜론으로 구분하기위해 포멧을 하나 더 만들었다.)
1004lucifer



4. 아래와 같이 화면이 변경된다.
  (하단에 Text / Data 구분하여 볼 수 있는 탭이 생긴다.)




참고
https://www.jetbrains.com/help/idea/edit-as-table-file-name-format-dialog.html

2017년 10월 12일 목요일

[Angular 2+] Form Submit 할 시 파일 포함하기 (multipart) - File Upload





Angular 로 파일 업로드 시 아래의 모듈을 많이 사용하는 것 같다.

1. ng2-file-upload
  - https://github.com/valor-software/ng2-file-upload

2. ng2-fileupload
  - https://github.com/thinkholic/ng2-fileupload


Angular 4

모듈이라는 개념에 맞게 순수하게 파일 업로드 기능만 들어있다.
저 모듈을 이용하게되면 우선 파일을 업로드 후 form submit 을 시켜야 할 것으로 보여진다.

서버 API 작업 시 multipart 로 text/file 파라메터를 동시에 받아 작업하는게 편한 경우가 있어 한번에 submit 시킬 수 있도록 작업을 했다.


작업환경: Angular 4

1004lucifer

multipart-form.service.ts
import {Injectable} from '@angular/core';
import {FormGroup} from '@angular/forms';
import {Headers, Http, Response} from '@angular/http';
import { Observable } from 'rxjs/Rx';

@Injectable()
export class MultipartFormService {

    constructor(
        private http: Http
    ) {
    }

    /**
     * Form의 string / File 파라메터를 Multipart 로 묶어서 서버에 전송한다.
     * @param {string} url
     * @param {FormGroup} formGroup
     * @param {Array<{name: string; file: File}>} files - [{'name': 'fileKey1', 'file': this.file1}, ...]
     * @returns {Observable<Response>}
     */
    sendMultipartForm(url: string, formGroup: FormGroup, files?: Array<{name: string, file: File}>): Observable<Response> {

        const formData = new FormData();

        for (const key in formGroup.value) {
            if (formGroup.value.hasOwnProperty(key)) {
                formData.append(key, formGroup.value[key]);
            }
        }

        if (files) {
            files
                .filter((file) => file.name != null && file.file != null)  // 정상적이지 않은 객체 Filter
                .forEach((file) => {
                    formData.append(file.name, file.file);
                });
        }

        const headers = new Headers({'enctype': 'multipart/form-data'});
        return this.http.post(url, formData, { headers });
    }
}






사용 (실제 사용된 소스를 약간 수정했다.)


use.component.html
<div>
    <form [formGroup]="myForm" (ngSubmit)="onSubmit()">
        <table class="tbl_w1 mrg_T20">
            <colgroup>
                <col width="150">
                <col width="">
            </colgroup>
            <tr>
                <td>param1</td>
                <td><input formControlName="param1" type="text" class="white_grayline input_wmiddle back_gray"></td>
            </tr>
            <tr>
                <td>param2</td>
                <td><input formControlName="param2" type="text" class="white_grayline input_wmiddle back_gray"></td>
            </tr>
            <tr>
                <td>file1</td>
                <td><input #file1Name class="file_input_textbox2" readonly/>
                    <div class="file_input_div">
                        <input type="button" value="찾아보기" class="file_input_button"/>
                        <input (change)="file1 = extractFile($event);
                                        file1Name.value = extractFileName($event)"
                               type="file" class="file_input_hidden"/>
                    </div>
                </td>
            </tr>
            <tr>
                <td>file2</td>
                <td><input #file2Name class="file_input_textbox2" readonly/>
                    <div class="file_input_div">
                        <input type="button" value="찾아보기" class="file_input_button"/>
                        <input (change)="file2 = extractFile($event);
                                        file2Name.value = extractFileName($event)"
                               type="file" class="file_input_hidden"/>
                    </div>
                </td>
            </tr>
        </table>

        <p class="aln_c mrg_T20">
            <button type="submit">확인</button>
        </p>
    </form>
</div>

1004lucifer

use.component.ts
import {Component, Input, OnInit} from '@angular/core';
import {NgbActiveModal} from '@ng-bootstrap/ng-bootstrap';
import {FormControl, FormGroup} from '@angular/forms';
import {MultipartFormService} from './multipart-form.service';

@Component({
    selector: 'use-selector',
    templateUrl: './use.component.html',
    styles: []
})
export class UseComponent implements OnInit {

    myForm: FormGroup;
    file1: File;
    file2: File;

    constructor(
        public activeModal: NgbActiveModal,
        private multipartFormService: MultipartFormService
    ) {
        this.myForm = new FormGroup({
            'param1': new FormControl(),
            'param2': new FormControl()
        });
    }

    ngOnInit() {
    }

    extractFile(event) {
        const files = event.target.files || event.srcElement.files;
        return files.length === 0 ? null : files[0];
    }
    extractFileName(event) {
        const file = this.extractFile(event);
        return file ? file.name : '';
    }

    onSubmit() {
        this.multipartFormService.sendMultipartForm(
            '/api/resourceName',
            this.myForm,

            // files 파라메터는 옵션으로 추가하지 않아도 된다.
            [{'name': 'fileKey1', 'file': this.file1}, {'name': 'fileKey2', 'file': this.file2}],
        ).subscribe(
            (response) => {
                console.log(response.json());
            },
            (response) => {
                console.log(response.json());
            });
    }
    /**
    onSubmit() {
        this.multipartFormService.sendMultipartForm(
            '/api/resourceName',
            this.myForm,
        ).subscribe(
            (response) => {
                console.log(response.json());
            },
            (response) => {
                console.log(response.json());
            });
    }
    */
}



use.module.ts
@NgModule({
    imports: [
        ReactiveFormsModule,
  ...
    ],
    declarations: [UseComponent],
    providers: [
        MultipartFormService
    ],
})
export class UseModule {
}



참고
- http://nberserk.github.io/default/2017/02/12/angular-form-file.html
  (사실상 위의 링크를 보고 만들었다.)
- https://www.angularjs4u.com/angularjs2/10-angular-2-file-upload-demos/





PS.
서버사이드를 Java Spring 을 사용하는데 위와같이 작업 시 이름이 같은 중복 파라메터는 보낼 수 없어 JSON 형식으로 객체를 보내는 방법으로 수정을 했는데 Content-Type 이슈로 인해 Spring 에서 JSON<=>객체 매핑이 제대로 되지 않았다.
아래와 같은 방법으로 Content-Type 헤더를 추가하여 이슈를 해결하였다.

링크: [Angular 2+] multipart 보낼 시 헤더에 Content-type 추가하기


[Angular 2+] Button 태그 없이 Submit 수행하기




Angular 4 에서 <a> (Anchor Tag) 로 Submit 을 해야 하는 상황이 생겼다.
(퍼블리싱된 페이지를 보니 <button> 이 아니라 <a> 로 되어있었다.)



아래와 같이 작업을 했다. (O)
1004lucifer
<form [formGroup]="myForm" (ngSubmit)="onSubmit()">
 <p class="aln_c mrg_T20">
  <button type="submit" #submitBtn style="display:none;"></button>
  <a (click)="submitBtn.click()"><span class="btn_blue btn_popcon">확 인</span></a>
  <a (click)="activeModal.close()"><span class="btn_gray btn_popcon">취 소</span></a>
 </p>
</form>





이것저것 해보며 아래와 같은 방법도 시도를 했는데,
아래와 같이 하게되면 페이지가 새로고침 되어버리므로 사용할 수가 없다. (X)

<form [formGroup]="myForm" (ngSubmit)="onSubmit()" #formElem>
 <p class="aln_c mrg_T20">
  <a (click)="formElem.click()"><span class="btn_blue btn_popcon">확 인</span></a>
  <a (click)="activeModal.close()"><span class="btn_gray btn_popcon">취 소</span></a>
 </p>
</form>


1004lucifer

PS.
(click) 속성으로 이렇게 저렇게 해보면서 한참을 시도 해봤는데 결국 button 태그가 꼭 있어야 ajax로 submit 이 가능했었다.ㅠ
다음엔 퍼블리싱 할 때 꼭 버튼은 button 태그를 이용해 달라고 이야기 해야 할듯..
 submit without but

2017년 10월 8일 일요일

[Angular 2+] 브라우저 현재 URL 가져오기





Angular 4 사용하면서 상단의 네비게이션이 현재 주소에 맞게 보여지도록(선택된) 보여야 하는데 현재 보여지는 URL을 가지고 구성하면 되겠다고 생각해서 아래와 같이 URL을 가져와 네비게이션 작업을 했다.

1004lucifer
get current url


menu.component.html
<div class="naviWrap">
 <ul class="navi">
  <li id="home" routerLink="/home/menu1" class="{{selectedMenu == 'home' ? 'on' : ''}}">홈</li>
  <li id="system" routerLink="/system/menu1" class="{{selectedMenu== 'system' ? 'on' : ''}}">시스템 설정</li>
 </ul>
</div>
<div id="homeSub" class="subnaviWrap" *ngIf="selectedMenu == 'home'">
 <ul class="subnavi">
  <li routerLink="/home/menu1" routerLinkActive="on">메뉴1</li>
  <li class="line">|</li>
  <li routerLink="/home/menu2" routerLinkActive="on">메뉴2</li>
  <li class="line">|</li>
  <li routerLink="/home/menu3" routerLinkActive="on">메뉴3</li>
 </ul>
</div>
<div id="homeSub" class="subnaviWrap" *ngIf="selectedMenu == 'system'">
 <ul class="subnavi">
  <li routerLink="/system/menu1" routerLinkActive="on">메뉴1</li>
  <li class="line">|</li>
  <li routerLink="/system/menu2" routerLinkActive="on">메뉴2</li>
  <li class="line">|</li>
  <li routerLink="/system/menu3" routerLinkActive="on">메뉴3</li>
 </ul>
</div>

1004lucifer


MenuComponent.ts
export class MenuComponent implements OnInit {

    selectedMenu: string;

    constructor(
        private router: Router,
    ) {
        this.selectedMenu = 'home'; // /home/menu1
    }

    ngOnInit() {
  // F5를 눌러 새로고침 시 MenuComponent가 초기화 되니 후 한번 실행
        const currentUrl = this.router.url.split('/');
        if (currentUrl[1]) {
            this.selectedMenu = currentUrl[1];
        }

  // menu.component.html 에서 routerLink 선택하여 메뉴(URL)이동 할 때 마다 실행
        this.router.events.subscribe((event) => {
            if (event instanceof NavigationEnd) {
                this.selectedMenu = event.url.split('/')[1];
            }
        });
    }
}


PS.
- Router Event Subscribe 사용 시 상황에 맞게 사용해야 한다.

(main)AppModule 의 bootstrap에 등록된 Component 에서 Router Event Subscribe 사용할 시 모든 페이지(URL) 이동 시 이벤트가 발생을 하고,
MenuComponent 에서 등록한 Router Event Subscribe 는 해당 컴포넌트에서 발생한 이벤트만 수행이 된다.


2017년 10월 6일 금요일

[SSMS] '데이터베이스 복원' 작업 시 멈춤 현상





OS: Windows Server 2012 R2
DB: SQL Server 2016 Developer
Tool: SSMS(SQL Server Management Studio) 17






다른 PC(Server)의 MS SQL Server 백업한 파일을 복원하기 위해 아래와 같이 데이터베이스 복원 기능을 이요하여 복원하려했다.
1004lucifer






백업된 파일을 선택하니 아래와 같은 화면에서 멈추고 더 이상 진행이 되지 않았다.








 인터넷으로 찾아봤는데 딱히 해결할 수 있는 명확한 방법이 나와있지 않고 SSMS를 repaire 하라는 이야기가 있었는데..
마침 사용하는 서버가 가상 서버이고 프로그램 설치 후 스냅샷을 생성한게 있어서 초기에 셋팅한 상태의 스냅샷 복구를 했다.

그동안 별다른 작업을 한것 같지는 않지만 별 문제 없이 다음으로 넘어가는 것을 확인 할 수 있었다.

추후 비슷한 문제 발생 시 SSMS 재설치를 한번 해봐야겠다.

1004lucifer




PS.
아래의 로그가 추후 문제를 찾는데 도움이 될지는 모르겠지만 로그를 추가로 첨부를 한다.
Log Event ID 검색 시 해결방법에 대한 다른 글을 찾지 못했다.


날짜 2017-10-04 오전 10:19:56
로그 Windows NT (Application)

원본 Windows Error Reporting
범주 (0)
이벤트 1001
컴퓨터 WIN-AAAAA

메시지
오류 버킷 , 유형 0
이벤트 이름: AppHangB1
응답: 사용할 수 없음
Cab ID: 0

문제 서명:
P1: Ssms.exe
P2: 2017.140.17177.0
P3: 5982ac65
P4: 84f5
P5: 67246336
P6: 
P7: 
P8: 
P9: 
P10: 

첨부 파일:

이 파일은 다음에서 사용할 수 있습니다.
C:\Users\Administrator\AppData\Local\Microsoft\Windows\WER\ReportQueue\Critical_Ssms.exe_5a36cc348872ae6636739daa81565639404590_d6281e50_13e4dda3

분석 기호: 
해결 방법 재확인: 0
보고서 ID: 1eed0c54-a8a2-11e7-80c4-08002755ea12
보고서 상태: 4100
해시된 패킷:



날짜 2017-10-04 오전 10:19:56
로그 Windows NT (Application)

원본 Application Hang
범주 (101)
이벤트 1002
컴퓨터 WIN-AAAAAAA

메시지
프로그램 Ssms.exe 버전 2017.140.17177.0에서 Windows와의 상호 작용을 중지했으며 해당 프로그램이 종료되었습니다. 문제에 대한 자세한 정보가 있는지 알아보려면 관리 센터 제어판에서 문제 기록을 확인하십시오.
 프로세스 ID: d10
 시작 시간: 01d33cada1efdc61
 종료 시간: 4294967295
 응용 프로그램 경로: C:\Program Files (x86)\Microsoft SQL Server\140\Tools\Binn\ManagementStudio\Ssms.exe
 보고서 ID: 1eed0c54-a8a2-11e7-80c4-08002755ea12
 오류 있는 패키지 전체 이름: 
 오류 있는 패키지에 상대적인 응용 프로그램 ID:




참고
https://connect.microsoft.com/SQLServer/feedback/details/3103750/ssms-2016-hangs-frequently
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/520b5cfe-f746-4d62-954b-df195cca97e4/ssms-2012-locks-up-in-restore-database-dialog?forum=sqlkjmanageability