eas build 를 사용하여 android 빌드를 수행중, 공식문서에 기재된 아래 프로필 설정의 차이점이 궁금해서 정리해본다.

  • developmentClient to true (default)
  • distribution to internal
  • android.buildType to apk
  • android.gradleCommand to :app:assembleRelease, :app:assembleDebug or any other gradle command that produces .apk
{
  "build": {
    "development": {
      "developmentClient": true
    },
    "android-apk-distribution": {
      "distribution": "internal"
    },
    "android-apk": {
      "android": {
        "buildType": "apk"
      }
    },
    "android-apk-gradle-debug": {
      "android": {
        "gradleCommand": ":app:assembleDebug"
      }
    },
    "production": {}
  }
}

 

 

1. developmentClient to true (defaul)
apk 파일로 빌드하며, apk 다운로드 링크가 제공된다. apk 빌드완료 후 expo-dev-clinet 를 사용하여 개발머신의 안드로이드 에뮬레이터에 띄우는 것이 특징이다. 그래서 expo-dev-client 가 설치되어 있지 않으면 설치를 요구한다. 개발머신에서 npx expo start --dev-client 커맨드를 사용하여 서버를 구동해야 하며, 에뮬레이터에서 해당 서버를 선택하여 사용한다.(아래 이미지참고)

 

 

 

 

 

2. distribution to internal

빌드된 apk 파일을 다운로드 할 수 있는 QR과 링크가 생성되며, 이에 접속하면 손쉽게 팀원들에게 apk를 전달할 수 있다.

 

 

 

 

3. android.buildType to apk

apk 파일로 빌드되며, 다운로드 받을 수 있는 링크가 제공된다.

 

 

 

4. android.gradleCommand to :app:assembleRelease, :app:assembleDebug

gradle 을 사용해서 apk를 빌드한다. 마찬가지로 apk 다운로드 링크가 제공된다.(또다른 차이점은 모르겠음)

 

블로그 이미지

망원동똑똑이

프로그래밍 지식을 자유롭게 모아두는 곳입니다.

,

리액트 네이티브 개초보로서(일주일차) 처음에는 직접 setState 를 하다가

노마드코더 님의 리액트 훅스 강좌를 보고, Node.js 그래프큐엘 강좌를 보고, 이를 연동하는 React Apollo 강좌를 보고

3연속 충격...

 

이건 꼭 써야한다.

 

리액트에서 그래프큐엘을 사용하는 방식은 2가지가 있다.

알기로는 최근에 공식적으로 리액트 훅스를 지원하게 되면서 기존에 더해서 한가지 방법이 추가된 것.

일단 샘플 프로젝트의 구조는 다음과 같다.

단순히 apolloClient.js 모듈을 App.js 에서 가져와서 적용해주고, 실제 그래프큐엘을 패치할 컴포넌트에서 쿼리를 가져와 사용하는 부분만 보면 된다(아래설명)

 

 

 

1. 필요모듈

*서버단은 이미 그래프큐엘 API로 제작되어있다고 가정

npm install @apollo/react-hooks apollo-boost graphql

기본적인 모듈 외에 위의 모듈을 추가로 설치해야한다.

 

 

 

2. 클라이언트 생성

appolloClient.js

import ApolloClient from "apollo-boost";
import utils from "./commons/utils";

const client = new ApolloClient({
    uri: utils.getApiServer
});

export default client;

위와 같이 apollo-boost 에서 클라이언트를 가져와 uri를 설정하며 생성한다. utils.getApiServer 는 단지 호스트:포트 를 반환하는 커스텀 모듈이다.(직접 주소를 써주면 됨)

 

 

 

3. 클라이언트 주입

App.js

import React from 'react';
import { ApolloProvider } from '@apollo/react-hooks';

// 사용자정의모듈
import client from "./src/apolloClient";
import Router from "./src/route";

export default function App() {
  return (
    <ApolloProvider client={client}>
      <Router />
    </ApolloProvider>
  );
}

App.js 파일에 ApolloProvider 를 가져온다. redux를 설정할 때와 비슷하게 ApolloProvider 태그에 위에서 만들어둔 객체를 client 속성에 주입해준다. Router 는 나의 경우 materialTopTabNavigator 위에 createStackNavigator 를 올려서 구성했다(모르겠으면 구글링 ㄱㄱ)

 

 

 

4. 쿼리예시

queries.js

import { gql } from "apollo-boost";
// import gql from 'graphql-tag';

export const HOME_PAGE = gql`
    query {
        people {
            id
            name
            age
            gender
        }
    }   
`;

예시는 Person 객체의 배열을 가져오는 쿼리다. gql 을 apollo-boost에서 가져오느냐 graphql-tag 에서 가져오느냐는 선택이지만, 최신 공식문서에서는 apollo-boost 에서 가져온다.(예전에는 graphql-tag만 사용했음)

여기서 만들어둔 쿼리를 필요한 컴포넌트에서 import해서 사용하게 된다.

 

 

 

5. 실제 컴포넌트에서의 사용방법 2가지(택1)

 

1) Query 태그 사용방법

Home.js

import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Query } from "react-apollo";

// 사용자정의모듈
import utils from "../commons/utils";
import { HOME_PAGE } from "../queries";

/**
 * 피드목록컴포넌트 - Query 태그를 사용하는 방식
 * @method
 */
const Home = () => 
    <Query query={HOME_PAGE}>
        {
            ({loading, data, error}) => {

                console.log("=============");
                console.log(loading);
                console.log(data);
                console.log(error);
                console.log("=============");

                let template = ``;
                if(loading) {template = <Text>`로딩중... ${loading}`</Text>;}
                if(error) {template = <Text>`에러발생 : ${error}`</Text>;}
                if(data && data.people){
                    template = data.people.map((item, index) => 
                        <Text key={index}>{item.id} / {item.name}</Text>
                    )
                }
                return (<View>{template}</View>);
            }
        }
    </Query>
export default Home;

 

2) useQuery 메서드 사용방법

Home2.js

import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { useQuery } from '@apollo/react-hooks';

// 사용자정의모듈
import utils from "../commons/utils";
import { HOME_PAGE } from "../queries";

/**
 * 피드목록컴포넌트 - useQuery 메서드를 사용하는 방식
 * @method
 */
const Home = () => {
    const { loading, error, data } = useQuery(HOME_PAGE);

    console.log("=============");
    console.log(loading);
    console.log(data);
    console.log(error);
    console.log("=============");
    
    let template = ``;
    if (loading) {template = <Text>`로딩중... ${loading}`</Text>;}
    if (error) {template = <Text>`에러발생 : ${error}`</Text>;}
    if (data && data.people) {
      template = data.people.map((item, index) => 
        <Text key={index}>{item.id} / {item.name}</Text>
      )
    }
    return (<View>{template}</View>);
}
export default Home;

자... 여기가 중요하다. 이부분 설명하려고 글쓰는것임.

두 컴포넌트는 완벽히 동일하게 작동하지만 문법이 다르다.

위의 방식은 react-apollo 모듈에서 Query 를 가져와 태그형식으로 가져온 데이터를 뷰에 그려준다. query={작성한쿼리} 속성을 주입하고, <Query> 태그 안에 스크립트문을 작성하는데, 이때 함수를 작성해주어야 하며, 이 함수가 반환한 jsx가 결과적으로 랜더린된다! Query 태그 안에서 함수를 작성해야 한다는 방식이 다소 생소하긴 하다.

 

두번째(아래) 방법은 좀더 언어적?인 것 같다. @apollo/react-hooks 에서 useQuery 를 가져와

const { loading, error, data } = useQuery(HOME_PAGE); 형식으로 쿼리를 인자로 넘겨 메서드를 호출한다.

그리고 data 를 가져와 jsx를 만들어 반환해주면 랜더링된다. 좀 더 우리에게 익숙한 방식인듯. 아마 그래서 최신에 react-hooks를 공식으로 지원하면서 이 방식을 권장할 것 같다...

 

결과

'Javascript > React Native' 카테고리의 다른 글

[React Native] android build profile 설정 차이 정리  (0) 2023.12.30
블로그 이미지

망원동똑똑이

프로그래밍 지식을 자유롭게 모아두는 곳입니다.

,

앵귤러2 에서 언젠가는 꼭 쓰게 되는 라이프사이클 훅에 대한 간단정리





ngOnChanges() : ngOnInt 보다 먼저, 하나 이상의 데이터 바인딩 된 프로퍼티가 변경될 때마다 호출. SimpleChanges 라는 인자와 함께 전달되며 SimpleChanges 안에는 previousValue 와 currentValue 라는 속성과 isFirstChange 라는 메서드가 존재. 최초 호출 시에는 isFirstChange 메서드는 true 를 반환, previousValue 는 UNINITIALIZED, currentValue 는 최초 바인딩된 값임.

* 참고 : https://angular.io/api/core/SimpleChange


ngOnInit() : 컴포넌트를 만든 직후 호출. 프로퍼티에 데이터가 바인딩 된 후 호출. 처음 한번만 호출됨.


ngDoCheck() : 컴포넌트의 상태 변경을 감지할 때마다 호출. 기본적으로 ngOnInit 이후 바로 한 번 호출. 구현된 컴포넌트에 관계없이 애플리케이션에 일어나는 모든 비동기 이벤트마다 실행되기 때문에 무리한 작업은 피하자.


ngAfterContentInit() : 컴포넌트의 뷰가 초기화되는 시점에 호출. Content Projection 으로 전달받은 템플릿의 초기화 완료 시 호출됨.


ngAfterContentChecked() : ngAfterContentInit 호출 이후 바로 호출됨. 뷰의 상태가 변경된 다음 처리할 것이 일을 때 사용됨.


ngAfterViewInit() : 컴포넌트 뷰와 자식 뷰를 초기화 한 후 실행. 즉, 부모로부터 프로퍼티 바인딩하여 받은 속성이 실제로 렌더링 완료되었을 시 호출됨.


ngAfterViewChecked() : ngAfterViewInit 호출 이후 바로 호출됨. 뷰의 상태가 변경된 다음 처리할 것이 일을 때 사용됨.


ngOnDestroy() : 컴포넌트가 소멸하기 직전에 실행. 보통 Observables 를 구독취소하여 메모리 누수 방지용으로 사용.





참고 블로그 : http://closer27.github.io/frontend/2017/07/06/Lifecycle-Hooks/

블로그 이미지

망원동똑똑이

프로그래밍 지식을 자유롭게 모아두는 곳입니다.

,

문제상황


앵귤러2 에서 일반적으로 자식 컴포넌트가 부모 컴포넌트에게 데이터를 전달하는 방법은 Output 과 EventEmitter 를 이용한 방법을, 부모 컴포넌트가 자식 컴포넌트에게 데이터를 전달하는 방법은 자식컴포넌트 선택자에 단지 속성으로 변수를 넘긴 후 Input 을 사용하여 받는다. 즉 뷰템플릿이 데이터 전송의 매체가 되는 셈.


하지만 문제가 복잡해져 서로 거리가 먼 컴포넌트 간의 통신을 해야하는 경우가 생김.

마치 나와 큰아빠와 큰아버지와 사촌동생 간에 동시에 데이터를 공유해야 하는 상황이라면?





1. 매개자 역할의 서비스 생성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { Injectable } from '@angular/core';
import { Subject, Observable } from 'rxjs';
 
@Injectable()
export class DataService {
  private subject = new Subject<any>();
 
  constructor() { }
 
  sendData(data){
    this.subject.next(data);
    console.log("sendData() data : " , data);
  }
 
  getData(){
    return this.subject.asObservable();
  }
}
 
cs


rxjs 의 Subject 객체는 Observable 인 동시에 Observer. 한 컴포넌트에서 서비스의 sendData() 를 호출하여 데이터를 Subject 의 next 메서드를 통해서 데이터 스트림에 밀어넣는다.


다른 getData() 메서드는 데이터를 받을 컴포넌트에서 호출하여 데이터스트림에서 Observable 객체를 받은 후, 데이터 전송이 완료되었을 때 구독(subscribe) 할 것이다.




2. 데이터를 보낼 컴포넌트

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import { Component, OnInit, OnDestroy } from '@angular/core';
import { PostService } from '../../services/post/post.service';
import { DataService } from '../../services/data.service';
 
import { Post } from '../../models/post';
import { ActivatedRoute, Params } from '@angular/router';
import { Subscription } from 'rxjs/Subscription';
 
@Component({
  selector: 'app-post',
  templateUrl: './post.component.html',
  styleUrls: ['./post.component.css']
})
export class PostComponent implements OnInit, OnDestroy {
  private subscription: Subscription;
  post: Post;
  postNo: number;
 
  constructor(private activatedRouter: ActivatedRoute, private postService: PostService, private dataService: DataService) {
    this.subscription = activatedRouter.params.subscribe((params: Params) => {
      this.postNo = params['postNo'];
      this.postService.getPost(this.postNo)
      .subscribe(
        (post) => {
          this.post = post;
          dataService.sendData(this.post);
        },
        (error) => {
          console.log(error);
        }
      );
    });
  }
 
  ngOnInit() {
    
  }
 
  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}
 
cs


좀 복잡해 보이지만 핵심은 단지 위에서 생성한 service 의 sendData() 를 호출하는 것. 참고로 subscription 객체는 구독했던 객체를 파괴하기 위해 사용. ngOnDestroy() 라이프사이클에서 저장했던 subscription 을 unsubscribe 해주면 된다.


DataService 를 주입받고, 보내고자 하는 데이터를 dataService.sendData() 로 호출하여 넘겨준다.




3. 데이터를 받을 컴포넌트

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import { Component, OnInit, OnDestroy } from '@angular/core';
import { DataService } from '../../services/data.service';
 
import { Subscription } from 'rxjs/Subscription';
 
@Component({
  selector: 'app-banner',
  templateUrl: './banner.component.html',
  styleUrls: ['./banner.component.css']
})
export class BannerComponent implements OnInit, OnDestroy {
  title = 'Blog';
  regDate = '';
  categoryName = '';
 
  subscription: Subscription;
 
  constructor(private dataService: DataService) {
    console.log("banner 컴포넌트 생성!");
    
    this.subscription = dataService.getData().subscribe(data => {
      console.log("banner subscription : " , data);
      this.title = data.title;
      this.regDate = data.regTime;
      this.categoryName = data.categoryId;
    })
 
  }
 
  ngOnInit() {
  }
 
  ngOnDestroy(){
    this.subscription.unsubscribe();
  }
 
}
 
cs


데이터를 수신할 컴포넌트에서도 마찬가지로 Subscription 객체를 이용해 구독한 객체를 파괴시켜주어야 하며, DataService 를 주입받고, 이 DataService 의 getData() 메서드를 호출하여 Observable 객체를 받는다. 이를 subscribe 메서드를 사용하여 데이터가 전달되면 수행할 작업을 진행하면 된다.




블로그 이미지

망원동똑똑이

프로그래밍 지식을 자유롭게 모아두는 곳입니다.

,

Node.js 의 실행된 프로세스에 관한 정보 얻기


node.js 의 process 객체에 있는 속성과 메서드에 대해서 알아본다.

물론 process 객체도 global 전역객체에 속해있다.




process 객체의 속성

속성 

설명 

 예시

 argv 

명령줄에서 실행할 때 입력한 매개변수를 나타내는 배열 

 [ 'C:\\Program Files\\nodejs\\node.exe',

  'D:\\csj\\js\\cluster\\cluster.js' ]

 env

 실행된 컴퓨터 시스템의 환경에 대한 정보 

 생략

 version

노드의 버전 

 v8.11.1

 versions 

이 노드 프로세스에서 사용하는 모듈들의 버전 

 { http_parser: '2.8.0',

  node: '8.11.1',

  v8: '6.2.414.50',

  uv: '1.19.1',

  zlib: '1.2.11',

  ares: '1.10.1-DEV',

  modules: '57',

  nghttp2: '1.25.0',

  openssl: '1.0.2o',

  icu: '60.1',

  unicode: '10.0',

  cldr: '32.0',

  tz: '2017c' }

 arch 

프로세서의 아키텍처 정보 

 x64

 platform 

플랫폼 정보 

 win32




process 객체의 메서드

 메서드

설명 

예시 

 exit([exitCode=0])

 프로그램 종료. 쉘 반환값이 0이면 정상종료, 1이면 비정상종료. 인자로 0 또는 1을 넘겨서 표시 가능

 생략

 memoryUsage()

 메모리 사용 정보 반환

 { rss: 22212608,

  heapTotal: 7684096,

  heapUsed: 5136152,

  external: 8608 }

 uptime()

 프로세스의 실행 시점부터 이 메서드를 호출한 시점까지의 시간 반환

 0.105


위 속성과 메서드들로 애플리케이션 동작 환경을 확인하여 상황에 맞게 코드를 구성할 수 있다.

memoryUsage() 와 uptime() 메서드는 웹애플리케이션의 로드밸런싱이나 재부팅에 사용될 수 있다고 함.




- 출처 : https://m.blog.naver.com/musasin84/60189885531

블로그 이미지

망원동똑똑이

프로그래밍 지식을 자유롭게 모아두는 곳입니다.

,

문제상황


앵귤러2에 텍스트 에디터 플러그인을 붙여 써야하는 상황이 발생.

처음 고른 에디터는 nhn에서 만든 Toast ui editor(일명 tui editor) 이었지만 이의 앵귤러 버전인 ngx-tui-editor 가 빌드시에 가용 메모리를 모두 점유하는? 문제가 발생하여 좀 더 안정성 있는 에디터인 Froala editor를 사용하기로 결정.


https://www.froala.com/wysiwyg-editor/docs/framework-plugins/angularjs-2-4


하지만 Docs 를 읽는 도중에 Reactive Form 이라는 용어가 등장하여 앵귤러2 Form 에 대해 간단히 찾아보고 내용을 정리하고자 함


(기본 사용법으로 해도 되지만 이미 궁금증이 생긴 상황)




1. Reactive Form VS Template From


앵귤러 2에서 폼의 작동원리는 2가지 방법으로 나뉨.

하나는 템플릿 기반의 폼으로, 각 input 태그에는 ngModel 속성을 주고, form 태그에서 #참조변수명="ngForm" 으로 (폼값에)로컬레퍼런스를 걸어 ngSubmit에 인자로 넘기는 방식인데, 나중에 따로 포스팅하기로 하고 Reactive Form만 살펴봄기로 함.

즉, Template Form 은 폼 설정이 html 템플릿에 있다고 할 수 있음




2. 모듈추가


Reactive Form을 사용하기 위해서는 app.module.ts 파일에 "ReactiveFormsModule" 을 가져와야 한다.


app.module.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
 
import { PostWriteComponent } from './components/post-write/post-write.component';
import { FormsModule, ReactiveFormsModule} from '@angular/forms';
 
import { HttpModule, RequestOptions } from '@angular/http';
 
@NgModule({
  declarations: [
  ],
  imports: [
    BrowserModule,
    HttpModule,
    FormsModule,
    ReactiveFormsModule
  ],
  providers: [
    ],
    bootstrap: [AppComponent]
})
export class AppModule { }
 
cs


필요 없는 부분은 뺏음. 

import { FormsModule, ReactiveFormsModule} from '@angular/forms';

부분을 추가하고 하단에 imports 시킨다.




3. 컴포넌트 파일에 Form 구조 작성


Reactive Form 에서는 html이 아닌 컴포넌트에서 직접 FormGroup 과 FormControl 을 설정해주어야 함.


post-write.component.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import { Component, OnInit } from '@angular/core';
 
import { FormGroup, FormControl } from '@angular/forms';
 
@Component({
  selector: 'app-post-write',
  templateUrl: './post-write.component.html',
  styleUrls: ['./post-write.component.css']
})
export class PostWriteComponent implements OnInit {
    postForm: FormGroup;
 
    constructor() {
        this.postForm = new FormGroup({
            'category'new FormControl(),
            'isNotice'new FormControl(),
            'title'new FormControl(),
            'content'new FormControl()
        });
    }
 
    ngOnInit() {}
    
    onSubmit(){
        console.log(this.postForm);
    }
 
}
 
cs


위와 같이 FormGroup 타입으로 폼값이 들어갈 객체를 생성해주는 동시에 FormControl로 프로퍼티를 지정해줌.

*onSubmit 함수는 아래에서 사용




4. html 폼 작성


html로 폼 테그를 짤 때에는 form 태그의 [formGroup]="컴포넌트에서생성한폼그룹객체" 와 input 태그의 formControlName 지시자만 잘 설정해주면 됨.

템플릿 기반 폼과는 달리 form 의 ngSubmit 이벤트 발생시 폼값을 html 에서 인자로 넘겨주는 것이 아니라, 이미 formGroup 과 formControlName 으로 바인딩되어있는 개체를 사용하기만 하면 된다.


post-write.component.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<h1>글 작성</h1>
<form [formGroup]="postForm" (ngSubmit)="onSubmit()">
    <div>
        <label for="category">카테고리</label>
        <input type="text" name="category" formControlName="category">
    </div>
    <div>
        <label for="isNotice">공지사항으로 설정</label>
        <input type="checkbox" name="isNotice" formControlName="isNotice">
    </div>
    <div>
        <label for="title">제목</label>   
        <input type="text" name="title" formControlName="title">
    </div>
    <div>
        <textarea [froalaEditor] formControlName="content"></textarea>    
    </div>
    <button type="submit">Submit</button>
</form>
cs



[formGroup]="postForm"

부분과

formControlName="category"

같은 부분이 핵심이다.




5. 빌드 후 데이터 확인


이제 ng build 후 브라우저 개발자도구 창에서 데이터를 확인한다.



이렇게 적당히 값을 입력한 후



서브밋을 하면 콘솔로 폼 값이 찍혀 나온다.

value 키에 실제 값들이 들어있는 것을 확인가능!


















블로그 이미지

망원동똑똑이

프로그래밍 지식을 자유롭게 모아두는 곳입니다.

,

문제상황


개인용 포폴로 새로운 웹사이트를 구축하기 시작하였습니다.

스택은 Angular2 + Node.js + Express + Mysql 일명 짝퉁 MEAN 스택이죠.

일단 AWS EC2 프리티어를 사용중에 있기 때문에 EC2 인스턴스 1개를 가지고 여러개의 서비스를 포트폴리오로 보여주고자 하였음.


그래서 EC2 서버에 nginx 리버스 프록시 서버를 올려서 특정 path 으로 들어오면 각각의 서비스를 보여주는 식으로.


예를 들면 

abc.com/aaa => 8080포트로 포워딩하여 aaa 라는 이름의 JSP 서비스로 연결!

abc.com/bbb => 3000포트로 포워딩하여 bbb 라는 이름의 node 서비스로 연결!

abc.com/ccc => 등등...


하지만 abc.com/bbb 에서 node 웹서버가 리스닝하는 3000포트로 날려주고, 노드서버는 앵귤러 페이지를 첫페이지로 뿌려줘야 하는데 문제가 생겼습니다.

앵귤러 프로젝트의 메인인 index.html은 나오지만, (기본 자식컴포넌트인) <app-root></app-root> 내용이 비어있는 것.


문제는 번들링된 파일들의 경로 문제였음.

아마도 리눅스 파일시스템 자원에 접근하는 경로가 기본적으로 /blog를 포함하지 않기 때문인 것.




1. base-href 와 deploy-url 에 대한 고민



위에 보면 <base href="/"> 태그가 존재함.

이부분은 아마 앵귤러 가상 url 라우팅에서 기본경로로 잡는 부분을 지정해주는 것 같음.


앵귤러 프로젝트를 빌드할 때


ng build -prod --base-href /test


로 빌드하면 위의 <base href="/test"> 로 빌드가 됨.


반면 deploy-url 은 정적 자원경로 앞에 prefix 형식으로 경로를 추가해 주는 것.





2. ng build -prod --base-href /blog --deploy-url /blog




ng build -prod --base-href /blog --deploy-url /blog 로 빌드한 경우 base href 속성과 정적 자원에 대한 경로 모두 /blog 가 붙으며, 정상적으로 모든 파일이 로드됨.





3. ng build -prod --base-href /blog



ng build -prod --base-href /blog 로 빌드한 경우 base href 속성이 /blog로 채워지지만, 정적 자원들은 그렇지 않아 번들링된 파일들이 로드되지 않음.





4. ng build -prod --deploy-url /blog



ng build -prod --deploy-url /blog 로 빌드한 경우 base href 의 속성은 채워지지 않았지만 정적 자원에 대한 경로는 제대로 잡힘. 현재 상황에서는 --deploy-url 옵션만 주는 것이 맞는 방법으로 보임.


하지만!


자세히 보니 url 에서 /blog 경로가 자동으로 제거된 것이 보임.

브라우저 특성이 아니라 자동으로 /blog 경로가 제거되며, 따라서 새로고침시에 기본경로로 접속하게 되어버림.


즉, base-href 옵션은 앵귤러 컴포넌트 라우팅에 사용할 기본 경로를 잡아주는 것임을 알 수 있다.


제대로 사용하기 위해서는 결국 ng build -prod --base-href /blog --deploy-url /blog 로 빌드해야 함





5. 또 다른 방법 APP_BASE_HREF 모듈 사용


위에서 나타난 url 상의 path 자동제거 현상을 잡아주는 모듈이 있음


import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { APP_BASE_HREF } from '@angular/common';

import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
import { HomeComponent } from './components/home/home.component';


@NgModule({
declarations: [
AppComponent,
HomeComponent
],
imports: [
BrowserModule,
RouterModule.forRoot([
{
path: 'home',
component: HomeComponent
}
])
],
providers: [{provide: APP_BASE_HREF, useValue: '/blog'}],
bootstrap: [AppComponent]
})


export class AppModule { }



3번 라인에서 APP_BASE_HREF 모듈을 가져오고, providers 에 userValue 값으로 원하는 경로를 설정.


이제 ng build -prod --deploy-url 옵션만 주어도 url 에서 경로가 지워지지 않는다.

(하지만 index.html 의 base href 속성은 왜 /blog 가 아닌 / 일까...)





블로그 이미지

망원동똑똑이

프로그래밍 지식을 자유롭게 모아두는 곳입니다.

,