본 글은 다음 글을 번역한 글입니다: The 2021 Web Development (Frontend + Backend) RoadMap

 

The 2021 Web Development (Frontend + Backend) RoadMap

An illustrated guide to becoming a Web Developer with links to relevant courses

dev.to

 

 

 

https://medium.com/@digitalact/2020-front-end-web-developer-road-map-ed3fcdec1c14

 

목차

 

  1. 2021/01/15 - [Programming] - 2021년 웹 개발 로드 맵 - 모든 개발자가 배워야 할 8가지.
  2. [현재 글] 2021년 웹 개발 로드 맵 - 프런트엔드
  3. 2021/01/15 - [Programming] - 2021년 웹 개발 로드 맵 - 백엔드

 

 

 

2021 프런트엔드 개발자 로드맵

 

2021년에 프런트엔드 개발자가 되고 싶다면 아래 프런트 엔드 개발자 로드맵을 따르는 게 좋습니다. 여기서 배우고 따라야 할 중요사항은 노란색 상자에 강조되어 있으며 이 상자에 집중하는 것이 좋습니다. 로드맵 아래에는 학습에 도움되는 책과 코스 같은 추가 리소스가 있습니다.

 

https://github.com/kamranahmedse/developer-roadmap/blob/master/img/frontend.png?year-2021-2

 

1. 웹 개발 기초.

 

웹 개발자가 되고 싶다면 인터넷, 웹 애플리케이션, HTTP와 같은 프로토콜 및 웹 개발 전반에 대한 기본 사항을 알아야 합니다. 고맙게도 이것들 뿐 아니라 그 이상을 배울 수 있는 수많은 강의들이 있습니다.

 

웹 개발을 배우고 싶은 모든 프로그래머들에게 다음 리소스를 권장합니다.

  1. The Web Developer Bootcamp by Colt Steel
  2. The 2021 Complete Web Developer Bootcamp by Angela Yu
  3. The Advanced Web Developer Bootcamp

 

 

2. HTML과 CSS

 

 

HTML과 CSS는 모든 웹사이트의 중심입니다. HTML은 구조를 제공하고 CSS를 스타일을 제공해 시각적으로 더 멋지게 보이도록 도와줍니다. 진정한 프런트엔드 개발자가 되고 싶다면 이 두 가지를 마스터해야 합니다. 이 두 가지를 시작하려면 이 무료 HTML과 CSS 강의를 확인해 온라인으로 배울 수 있습니다.

 

 

3. 자바스크립트

 

 

객체 지향 프로그래밍에서 추상화, 캡슐화, 다형성, 상속의 네 개의 기둥이 있는 것처럼 웹 개발에는 HTML, CSS, 자바스크립트라는 세 개의 주요 기둥이 있습니다.

 

처음 두 개는 구조와 스타일을 제공하지만 상호 작용을 추가해 생생하게 만드는 것은 자바스크립트입니다. 요즘의 자바 개발자가 자바 스크립트를 배우는 것은 매우 중요하며 고맙게도 초급 자바스크립트와 고급 자바 스크립트를 모두 배울 수 있는 많은 강의가 존재합니다. 다음 몇 가지 권장사항이 있습니다.

  1. The Complete JavaScript Cource 2021
  2. A Beginer's Guide to Advanced JavaScript and ES6

 

 

4. 타입 스크립트

 

 

C와 C++가 있는 것처럼 타입 스크립트는 C++ 만큼 인기가 많지 않지만 자바 스크립트++로 여겨질 수 있습니다. 타입 스크립트의 좋은 부분은 자바스크립트 코드에 타입 안정성을 추가해 개발 단계에 불쾌한 자바스크립트 타입 관련 오류를 확인할 수 있다는 것입니다.

 

또한 자바스크립트용 객체 지향 코드를 쉽게 개발할 수 있습니다. 타입 스크립트를 배우고 싶다면 "Understanding TypeScript"는 시작하기 좋은 강의입니다. 더 많은 선택 사항이나 무료 강의가 필요한 경우 시작할 수 있는 또 다른 무료 타입 스크립트 강의가 더 있습니다.

 

 

5. 앵귤러

 

 

사람들이 일반적인 HTML, CSS, 자바스크립트를 사용해 웹사이트를 구축하던 시대는 지났습니다. 요즘 대부분의 작업은 앵귤러, 리액트, 뷰 js와 같은 프레임워크에서 수행됩니다. 코드를 저장하기 위한 구조를 제공해 줄 뿐 아니라 웹 애플리케이션을 빠른 시간 내 쉽게 개발할 수 있습니다. 

 

앵귤러는 구글에서 지원하므로 앵귤러를 배우는 것은 모든 최산 웨 개발자에게 좋은 선택입니다. 앵귤러를 배우고 싶다면 Max의 "Angular - The Complete Guide"보다 좋은 과정은 없을 겁니다.

 

 

6. 리액트 

 

앵귤러와 마찬가지로 리액트는 웹 애플리케이션 개발에 널리 사용되는 라이브러리입니다. 재사용 가능한 컴포넌트를 작성하여 현대적인 인터랙티브 웹 페이지를 만드는 데 사용됩니다.

 

앵귤러가 구글에서 지원하는 것처럼 리액트는 페이스북에서 지원하므로 유명합니다. 리액트를 배우고 싶다면 Stepgen Grider의 "Modern React with Redux" 또는 Max의 "React - The Complete Guide on Udemy"에 참여하세요. 둘 다 온라인에서 리액트를 배울 수 있는 훌륭한 과정입니다. 링크는 다음과 같습니다:

  1. Modern React with Redux
  2. React - The Complete Guide

 

 

7. 뷰 js

 

 

뷰 또는 뷰 JS는 작년에 속도에 대한 성능이 개선되었으며 점점 더 많은 사람들이 뷰 js를 추천하고 상요하고 있습니다. 저는 여전히 뷰의 초보자이며 아마도 뷰를 배우고 뷰 기반의 애플리케이션으로 마이그레이션 하는데 시간이 걸릴 것입니다. 그러나 이미 뷰 js를 배우기로 결정을 내린 경우 Udemy의 Vue.js Essentials 강의부터 시작할 수 있습니다.

 

 

 

 

 

 

 

반응형

 

이 글은 다음 영상의 튜토리얼을 글로 풀어쓴 내용을 담고 있습니다: Intro to Vue.js: Build a Todo App

 

 

 

1. 앞선 글.

 

 

이 글에선 위의 링크에서 안내되는 튜토리얼을 따라 해 볼 예정입니다. 이 영상에서 소개하는 내용은 가장 기초적인 내용으로 Vue JS의 기능을 이용해 Todo list를 관리하는 앱을 만드는 것입니다.

 

소개된 튜토리얼의 난이도는 초심자 급으로 VueJS를 갓 시작한 개발자를 위한 튜토리얼입니다. 영상의 길이는 약 19분으로 짧은 편이므로 영상을 직접 보시는 것을 추천드립니다.

 

또한 모든 소스코드는 다음 경로에서 확인할 수 있습니다: CodingGarden/vue-todo

 

CodingGarden/vue-todo

Intro to Vue.js: Build a Todo App. Contribute to CodingGarden/vue-todo development by creating an account on GitHub.

github.com

 

 

 

2. 목표 알아보기.

 

이 튜토리얼은 다음과 같은 순서로 진행됩니다. 

 

  • Index.html 생성.
  • Index.html에 Vue.js 추가하기.
  • app.js에 뷰 인스턴스 생성하기.
  • 페이지에 뷰 인스턴스로부터 메시지 표시하기.
  • 새로운 Todo 폼 생성하기.
  • 폼이 제출되었을 때 함수를 호출하기.
  • 유저가 입력한 값을 저장하기 위한 문자열 속성 생성하기.
  • Todo를 저장할 배열 속성 생성하기.
  • Todo항목을 Todos 배열에 넣고 완료 상태를 false로 설정하기.
  • Todos의 값을 화면에 보여주기.
  • Todo 완료 체크박스를 만들기.
  • Todo 완료 체크박스가 체크된 경우 Todo에 취소선을 추가하기.
  • Todo를 삭제하는 버튼을 추가하기.
  • 모든 Todo를 완료하는 버튼을 추가하기.

 

 

 

3. 뷰 프로젝트 생성.

 

뷰 프로젝트를 생성하는 방법은 여러 가지가 있습니다. 이 글에선 index.html이라는 정적 파일을 생성 한 뒤 vue.js를 script에 추가한 뒤 app.js에서 뷰 인스턴스를 생성해 사용하는 방법에 대해 알아보도록 합니다.

 

다음 명령어로 프로젝트 폴더를 만들고 index.html 파일을 생성해 주세요.

 

mkdir todo-app
cd todo-app
touch index.html

 

그리고 생성한 index.html을 다음과 같이 코딩해 주세요.

 

<!DOCTYPE html>
<html lang="en" dir="ltr">
    <head>
        <meta charset="utf-8">
        <title></title>
    </head>
    <body>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    </body>
</html>

 

index.html을 생성하고 vue.js를 추가했습니다. 지금 우리는 vue.js의 개발 버전을 추가한 상황입니다. 추가할 vuejs에 대해서는 공식 홈페이지를 참고해 주세요: VueJS의 설치방법 - 직접 script에 추가.

 

 

 

4. 뷰 인스턴스 생성.

 

이제 뷰 인스턴스를 생성할 준비가 되었습니다. app.js 파일을 생성하고 다음 코드처럼 뷰 인스턴스를 생성해 보도록 합시다.

 

// app.js
const app = new Vue({
    el: '#app',
    data: {},
    methods: {},
});

 

위의 코드는 뷰 인스턴스를 생성해주는 코드입니다. el에 id를 정의해 주었으며 이 id를 가진 엘리먼트에 해당 뷰 인스턴스가 적용됩니다. 그러면 이제 app이라는 id를 가지는 엘리먼트를 index.html에 추가해 줍시다.

 

<!DOCTYPE html>
<html lang="en" dir="ltr">
    <head>
        <meta charset="utf-8">
        <title></title>
    </head>
    <body>
        <main id="app">
        </main>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <script src="app.js"></script>
    </body>
</html>

 

main에 id를 app으로 지정해 주었으며 app.js에 대한 참조도 추가했습니다. 우리의 뷰가 제대로 동작하는지 한번 확인해 봅시다. app.js를 다음과 같이 수정해 주세요.

 

// app.js
const app = new Vue({
    el: '#app',
    data: {
        title: 'Hello VueJS',
    },
    methods: {},
});

 

data 객체에 title이란 문자열을 추가했습니다. 정상적으로 뷰 인스턴스를 생성했다면 html에서 해당 데이터를 가져다 사용할 수 있습니다. 이때 뷰 인스턴스에서 가져온 데이터라는 것을 표시하기 위해 중괄호 두 개를 사용합니다. {{title}}과 같이요. 다음과 같이 index.html을 수정 해 데이터를 가져와 표시해 보도록 합시다.

 

<!-- index.html -->
<!DOCTYPE html>
<html lang="en" dir="ltr">
    <head>
        <meta charset="utf-8">
        <title></title>
    </head>
    <body>
        <main id="app">
            <h3>{{title}}</h3>
        </main>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <script src="app.js"></script>
    </body>
</html>

 

그리고 index.html 파일을 열면 정상적으로 title에 입력한 문자열을 출력하는 것을 확인할 수 있습니다.

 

 

 

5. Todo를 위한 폼 생성하기.

 

이제 본격적으로 TodoList를 위한 작업을 시작하도록 합시다. 가장 먼저 Todo 작업을 입력받을 텍스트 박스와 제출 버튼을 추가해 보도록 합시다. index.html을 다음과 같이 수정해 주세요.

 

<!-- index.html -->
<!DOCTYPE html>
<html lang="en" dir="ltr">
    <head>
        <meta charset="utf-8">
        <title></title>
        <link rel="stylesheet" href="https://ez-css.now.sh">
    </head>
    <body>
        <main id="app">
            <h3>{{title}}</h3>
            <form>
                <label for="newTodo">New Todo</label>
                <input type="text" name="newTodo" value="">
                <button type="submit" name="button">Add</button>
            </form>
        </main>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <script src="app.js"></script>
    </body>
</html>

 

기본적인 스타일 시트를 제공해주는 사이트를 추가했습니다. 또한 h3아래에 form을 추가했습니다. 이제 제출 버튼이 클릭되었을 때 호출되는 함수를 정의해 주도록 합시다. app.js를 다음과 같이 수정해 주세요.

 

// app.js
const app = new Vue({
    el: '#app',
    data: {
        title: 'Hello VueJS',
    },
    methods: {
        addTodo: function(){
            console.log(`Todo is submitted.`)
        },
    },
});

 

앞으로 뷰에서 사용할 함수는 methods 객체에 추가하면 사용할 수 있습니다. addTodo함수를 추가했으며 해당 함수가 호출되면 콘솔 창에 메시지를 출력하도록 했습니다. 이제 이 함수를 폼에 바인드 해보도록 합시다. index.js의 폼을 다음과 같이 수정해 주세요.

 

<!-- index.html -->
...
            <form @submit.prevent="addTodo">
                <label for="newTodo">New Todo</label>
                <input type="text" name="newTodo" value="">
                <button type="submit" name="button">Add</button>
            </form>
...

 

@를 이용해 이벤트를 바인드 할 수 있습니다. .prevent는 제출 이벤트가 페이지를 다시 로드하는 것을 방지해줍니다. 폼이 제출되면 addTodo가 호출되고 페이지가 다시 로드되지 않도록 작성되었습니다. 페이지로 이동해 폼을 제출해 보세요.

 

 

우리가 작성한 메시지가 정상적으로 보입니다.

 

 

 

6. Todo 저장하기.

 

이제 함수가 호출되는 것을 확인했으니 Todo를 추가해 보도록 하겠습니다. app.js를 다음과 같이 수정해 주세요.

 

// app.js
const app = new Vue({
    el: '#app',
    data: {
        title: 'Hello VueJS',
        newTodo: '',
        todos: [],
    },
    methods: {
        addTodo: function(){
            this.todos.push({
                title: this.newTodo,
                done: false,
            });
            this.newTodo = '';
        },
    },
});

 

data에 newTodo와 todos가 추가되었습니다. newTodo는 사용자가 입력할 새 작업입니다. 텍스트 박스에 입력될 것입니다. todos는 현재 사용자가 추가한 todo의 배열입니다. 사용자가 todo를 추가하면 이 배열에 저장됩니다.

 

methods에 addTodo도 같이 수정되었습니다. 해당 인스턴스의 todos에 새로운 객체를 생성해 push 하게 됩니다. 해당 객체에는 title과 done을 가지며 title에 사용자가 입력한 작업이 저장되며 done은 해당 작업이 완료되었는지에 대한 플래그가 저장됩니다. 새로운 todo가 추가되면 기존 텍스트 박스의 값을 비워주는 로직도 추가하였습니다. 

 

이제 우리가 구현한 기능이 적용될 수 있도록 index.html을 수정해 줍시다.

 

<!-- index.html -->
<!DOCTYPE html>
<html lang="en" dir="ltr">
    <head>
        <meta charset="utf-8">
        <title></title>
        <link rel="stylesheet" href="https://ez-css.now.sh">
    </head>
    <body>
        <main id="app">
            <h3>{{title}}</h3>
            <form @submit.prevent="addTodo">
                <label for="newTodo">New Todo</label>
                <input v-model="newTodo" type="text" name="newTodo" value="">
                <button type="submit" name="button">Add</button>
            </form>
            <ul>
                <li v-for="todo in todos">
                    <input type="checkbox" v-model="todo.done">
                    <span>{{todo.title}}</span>
                </li>
            </ul>
        </main>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <script src="app.js"></script>
    </body>
</html>

 

form 내부의 input에 v-model이 추가된 것을 확인할 수 있습니다. 이 명령어를 통해 우린 이 input의 값을 newTodo에 바인딩 할 수 있습니다.

 

아래의 li에 보면 v-for이 추가되어 있습니다. 이는 C#의 foreach와 매우 유사하게 동작합니다. todo in todos에서 우리는 해당 루프 문을 수행할 때 뷰 인스턴스의 todos에 들어 있는 내용을 토대로 루프문을 실행합니다. 즉 todos의 위치에 있는 내용은 뷰 인스턴스에 동일하게 정의가 되어 있어야 함을 의미합니다. todo는 우리가 html내에서 정의하면 됩니다. 이 값을 내부에서 사용하도록 합니다.

 

내부의 체크박스는 todo의 done 속성을 반영하고 있으며 span에선 유저가 입력한 newTodo(todos에 저장되면 title) 값을 보여줍니다.

 

 

아름답진 않지만 정상적으로 보이고 있습니다.

 

 

 

7. 완료 및 삭제 처리 하기.

 

이제 체크박스를 선택하면 타이틀에 취소선이 추가되는 기능과 Todo를 삭제하는 기능, 현재 추가된 모든 Todo를 완료하는 기능을 추가해 보도록 하겠습니다.

 

app.js를 열고 todo를 삭제하는 로직을 추가해 주세요.

 

// app.js
const app = new Vue({
...
    methods: {
        addTodo: function(){
            this.todos.push({
                title: this.newTodo,
                done: false,
            });
            this.newTodo = '';
        },
        removeTodo: function(todo) {
            const todoIdx = this.todos.indexOf(todo);
            this.todos.splice(todoIdx, 1);
        },
    },
});

 

로직은 간단합니다. 매개변수로 받아온 todo가 todos에서 어디 있는지 찾아낸 후 그 하나를 없애버리는 것이 전부입니다. 이제 이 함수를 index.html에 반영해 봅시다.

 

<!-- index.html -->
...
                <li v-for="todo in todos">
                    <input type="checkbox" v-model="todo.done">
                    <span>{{todo.title}}</span>
                    <button @click="removeTodo(todo)" type="button">Remove</button>
                </li>
...

 

@click을 추가해 해당 버튼이 클릭되었을 때 removeTodo(todo)가 호출되게 만들었습니다. 이제 페이지를 실행시킨 뒤 새 todo를 입력해보면 다음과 같이 보입니다.

 

 

Todo에 추가된 remove버튼을 클릭하면 해당 Todo가 삭제됩니다. 

 

이제 완료 기능을 만들어 보도록 하겠습니다. 체크박스를 클릭하면 Todo가 완료되었다고 판단하고 유저가 입력한 Todo에 취소선을 추가해 완료가 되었다는 표시를 남기도록 해보겠습니다. 또한 이와 함께 모든 작업을 한 번에 완료하는 버튼도 같이 추가해 보도록 하겠습니다.

 

app.js를 다음과 같이 수정해 주세요. 이제 마지막 코드 수정입니다!

 

// app.js
const app = new Vue({
    el: '#app',
    data: {
        title: 'Hello VueJS',
        newTodo: '',
        todos: [],
    },
    methods: {
        addTodo: function(){
            this.todos.push({
                title: this.newTodo,
                done: false,
            });
            this.newTodo = '';
        },
        removeTodo: function(todo) {
            const todoIdx = this.todos.indexOf(todo);
            this.todos.splice(todoIdx, 1);
        },
        allDone: function(){
            this.todos.forEach((todo) => {
                todo.done = true;
            });
        },
    },
});

 

마지막으로 allDone 함수를 추가했습니다. 모든 todos를 순회하면서 done값을 true로 변경합니다.

 

이제 index.html을 수정합시다.

 

<!-- index.html -->
<!DOCTYPE html>
<html lang="en" dir="ltr">
    <head>
        <meta charset="utf-8">
        <title></title>
        <link rel="stylesheet" href="https://ez-css.now.sh">
        <style>
            .done{
                text-decoration: line-through;
            }
        </style>
    </head>
    <body>
        <main id="app">
            <h3>{{title}}</h3>
            <form @submit.prevent="addTodo">
                <label for="newTodo">New Todo</label>
                <input v-model="newTodo" type="text" name="newTodo" value="">
                <button type="submit" name="button">Add</button>
            </form>
            <button @click="allDone" type="button" name="button">All Done</button>
            <ul>
                <li v-for="todo in todos">
                    <input type="checkbox" v-model="todo.done">
                    <span :class="{done: todo.done}">{{todo.title}}</span>
                    <button @click="removeTodo(todo)" type="button">Remove</button>
                </li>
            </ul>
        </main>
        <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
        <script src="app.js"></script>
    </body>
</html>

 

style이 추가되었습니다. .done 클래스가 추가된 항목은 취소선을 추가하도록 하였습니다. 

 

새 버튼이 추가되었습니다. 버튼을 클릭하면 앞서 구현한 allDone함수가 호출됩니다.

 

li의 span에 클래스 바인딩이 추가되었습니다. 만약 내부 조건(:이후의 조건)이 true라면 done이라는 클래스가 추가되도록 하는 논리입니다. 즉 todo.done이 true인 경우 해당 span에 done라는 클래스가 추가됩니다.

 

이제 페이지를 열고 한번 테스트해보세요!

 

 

구현한 기능이 모두 정상적으로 동작하는 것을 확인할 수 있습니다!

 

 

 

 

 

반응형

이 글은 다음 영상의 튜토리얼을 글로 풀어쓴 내용을 담고 있습니다: Build a Weather App in VueJS | VueJS Beginner Tutorial

 

 

 

1. 앞선 글.

 

이 글에선 위의 링크에서 안내되는 튜토리얼을 따라 해 볼 예정입니다. 이 영상에서 소개하는 내용은 외부 API를 사용해 검색한 도시의 날씨 정보를 보여주는 앱을 작성하는 것입니다.

 

소개된 튜토리얼의 난이도는 초심자 급으로 VueJS를 갓 시작한 개발자를 위한 튜토리얼입니다. 영상의 길이는 약 27분으로 짧은 편이므로 영상을 직접 보시는 것을 추천드립니다.

 

또한 모든 소스코드는 다음 경로에서 확인할 수 있습니다: TylerPottsDev/weather-vue

 

TylerPottsDev/weather-vue

Contribute to TylerPottsDev/weather-vue development by creating an account on GitHub.

github.com

 

 

 

2. 날씨 API 준비하기.

 

외부에서 날씨정보를 읽어오기 위해 OpenWeatherMap라는 사이트를 사용할 예정입니다. 여기로 이동해 회원가입을 진행해 주세요.

 

회원가입 후 위의 그림처럼 API -> Current Weather Data -> Subscribe를 클릭합니다.

 

 

별도의 유료 플랜은 선택사항입니다. 우린 무료 플랜을 사용할 예정입니다. 위의 표시된 Get APU key를 클릭해 주세요.

 

 

전 이미 키를 받았기 때문에 이미 키가 있다는 알림이 표시됩니다. 키를 발급받은 후 메인 페이지에서 볼 수 있는 API Keys 메뉴로 이동합니다. 위의 가려진 곳에 우리가 발급받은 API 키가 존재합니다.

 

만약 새로운 키를 발급받고 싶으시다면 우측의 Create Key에 원하는 이름을 입력 후 Generate버튼을 클릭해 새로운 키를 발급받을 수 있습니다.

 

키를 여기서 확인할 수 있다는 사실을 기억해 두세요!

 

 

 

3. VueJS 프로젝트 만들기.

 

VueJS 프로젝트를 생성하는 방법은 여러 방법이 있습니다만 여기에서는 yarn과 vue-cli을 이용해 프로젝트를 생성하는 방법에 대해 알아보도록 하겠습니다. 

 

** yarn을 설치하는 방법에 대해선 이 글에서 설명하지 않습니다. 설치 방법은 yarn 공식 홈페이지의 설치 문서를 참고해 주세요.

 

yarn을 설치했다면 다음 명령어를 통해 vue-cli를 글로벌로 설치합니다.

 

yarn global add @vue/cli

 

 

위와 같이 vue-cli 설치가 완료되면 이제 vue 프로젝트를 생성할 준비가 다 되었습니다. 다음 명령어를 통해 vue 프로젝트를 생성해 봅시다.

 

vue create weather-app

 

위 명령어를 입력하면 다음과 같이 프리셋을 고르는 옵션이 보입니다. 

 

 

특별히 원하는 옵션이 없는 경우 Vue2를 사용하는 Default 옵션을 선택해 줍시다.

 

 

약간의 시간이 지난 후 위와 같이 Vue 프로젝트가 생성되게 됩니다. 안내에 표시된 대로 다음 명령어를 통해 프로젝트를 실행시켜 봅시다.

 

cd weather-app
yarn serve

 

 

정상적으로 실행이 되었다면 http://localhost:8080/으로 이동해 다음과 같은 기본 페이지를 확인할 수 있습니다.

 

 

 

 

4. 페이지 가다듬기.

 

이제 기본 페이지에서 불필요한 파일과 코드를 제거하고 날씨 정보를 표시해 줄 수 있도록 페이지를 수정해 보도록 하겠습니다.

 

우선 여기로 이동해 백그라운드 이미지를 다운로드하여줍니다. cold-bg.jpg와 warm-bg.jpg파일을 다운 받아 asset폴더에 저장해 주세요. 그리고 assets 폴더 내의 logo.png파일을 삭제한 뒤 App.vue 파일을 열어 내용을 다음과 같이 수정해 주세요.

 

<template>
  <div id="app">
    <main>
      <div class="search-box">
        <input type="text" class="search-bar" placeholder="Search..."/>
      </div>
    </main>
  </div>
</template>

<script>
export default {};
</script>

<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
body {
  font-family: 'montserrat', sans-serif;
}
#app {
  background-image: url('./assets/cold-bg.jpg');
  background-size: cover;
  background-position: bottom;
  transition: 0.4s;
}
main {
  min-height: 100vh;
  padding: 25px;
  background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.25), rgba(0, 0, 0, 0.75));
}
.search-box {
  width: 100%;
  margin-bottom: 30px;
}
.search-box .search-bar {
  display: block;
  width: 100%;
  padding: 15px;
  
  color: #313131;
  font-size: 20px;
  appearance: none;
  border:none;
  outline: none;
  background: none;
  box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.25);
  background-color: rgba(255, 255, 255, 0.5);
  border-radius: 0px 16px 0px 16px;
  transition: 0.4s;
}
.search-box .search-bar:focus {
  box-shadow: 0px 0px 16px rgba(0, 0, 0, 0.25);
  background-color: rgba(255, 255, 255, 0.75);
  border-radius: 16px 0px 16px 0px;
}
</style>

 

파일을 저장하고 다시 localhost:8080으로 이동해 봅시다.

 

 

화면이 위와 같이 바뀐 게 보이시나요? 이제 우리는 임시 데이터를 갖고 날씨 정보를 표시해 볼 예정입니다. 다시 App.vue를 열고 코드를 수정해 주세요.

 

<template>
  <div id="app">
    <main>
      <div class="search-box">
        <input type="text" class="search-bar" placeholder="Search..." />
      </div>
      <div class="weather-wrap">
        <div class="location-box">
          <div class="location">Northampton, UK</div>
          <div class="date">Monday 20 January 2020</div>
        </div>
        <div class="weather-box">
          <div class="temp">9℃</div>
          <div class="weather">Rain</div>
        </div>
      </div>
    </main>
  </div>
</template>

<script>
export default {};
</script>

<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
body {
  font-family: "montserrat", sans-serif;
}
#app {
  background-image: url("./assets/cold-bg.jpg");
  background-size: cover;
  background-position: bottom;
  transition: 0.4s;
}
main {
  min-height: 100vh;
  padding: 25px;
  background-image: linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0.25),
    rgba(0, 0, 0, 0.75)
  );
}
.search-box {
  width: 100%;
  margin-bottom: 30px;
}
.search-box .search-bar {
  display: block;
  width: 100%;
  padding: 15px;

  color: #313131;
  font-size: 20px;
  appearance: none;
  border: none;
  outline: none;
  background: none;
  box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.25);
  background-color: rgba(255, 255, 255, 0.5);
  border-radius: 0px 16px 0px 16px;
  transition: 0.4s;
}
.search-box .search-bar:focus {
  box-shadow: 0px 0px 16px rgba(0, 0, 0, 0.25);
  background-color: rgba(255, 255, 255, 0.75);
  border-radius: 16px 0px 16px 0px;
}
.location-box .location {
  color: #fff;
  font-size: 32px;
  font-weight: 500;
  text-align: center;
  text-shadow: 1px 3px rgba(0, 0, 0, 0.25);
}
.location-box .date {
  color: #fff;
  font-size: 20px;
  font-weight: 300;
  font-style: italic;
  text-align: center;
}
.weather-box {
  text-align: center;
}
.weather-box .temp {
  display: inline-block;
  padding: 10px 25px;
  color: #fff;
  font-size: 102px;
  font-weight: 900;
  text-shadow: 3px 6px rgba(0, 0, 0, 0.25);
  background-color: rgba(255, 255, 255, 0.25);
  border-radius: 16px;
  margin: 30px 0px;
  box-shadow: 3px 6px rgba(0, 0, 0, 0.25);
}
.weather-box .weather {
  color: #fff;
  font-size: 48px;
  font-weight: 700;
  font-style: italic;
  text-shadow: 3px 6px rgba(0, 0, 0, 0.25);
}
</style>

 

위와 같이 코드를 수정하고 다시 localhost:8080로 이동해 보세요.

 

 

우리가 직접 넣은 데이터가 잘 표시되는 것을 확인할 수 있습니다.

 

 

 

5. API를 통해 데이터를 불러오기.

 

소스코드를 찬찬히 살펴보면 사실 지금까지 우리가 작성한 코드는 단지 html 약간과 화면을 꾸미기 위한 css가 전부임을 확인할 수 있습니다.

 

이제 우리가 앞에서 발급받은 API 키를 이용해 날씨 정보를 받아와 화면에 뿌려주는 로직을 작성해 보도록 하겠습니다.

 

먼저 App.vue의 script 영역을 다음과 같이 수정해 줍시다.

 

<script>
export default {
  data: function () {
    return {
      api_key: "YOUR_API_KEY_HERE",
      url_base: "https://api.openweathermap.org/data/2.5/",
      query: "",
      weather: {},
    };
  },
  methods: {
    fetchWeather: function (e) {
      if (e.key == "Enter") {
        let fetchUrl = `${this.url_base}weather?q=${this.query}&units=metric&APPID=${this.api_key}`;
        fetch(fetchUrl)
          .then((res) => {
            console.log(res);
            return res.json();
          })
          .then((results) => {
            return this.setResult(results);
          });
      }
    },
    setResult: function (results) {
      this.weather = results;
    },
    dateBuilder: function () {
      let d = new Date();
      let months = [
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December",
      ];
      let days = [
        "Sunday",
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday",
        "Saturday",
      ];
      let day = days[d.getDay()];
      let date = d.getDate();
      let month = months[d.getMonth()];
      let year = d.getFullYear();
      return `${day} ${date} ${month} ${year}`;
    },
  },
};
</script>

 

위에서부터 차근차근 보도록 합시다.

 

data 함수에는 vue 컴포넌트에서 사용할 데이터가 담겨 있습니다. 우리는 이제 이 컴포넌트에서 api_key, url_base, query, weather을 사용할 수 있습니다.

  • api_key에는 앞서 우리가 발급받은 API 키값을 넣어 주시면 됩니다.
  • url_base는 API를 호출할 URL입니다.
  • query는 search시 우리가 입력한 데이터가 들어갈 공간입니다.
  • weather은 검색 결과 데이터가 들어갈 공간입니다.

 

methods에는 이 컴포넌트에서 사용할 method들이 정의되어 있는 곳입니다.

  • fetchWeather 함수는 search에 값을 입력하고 엔터를 누를 경우 해당 값을 사용해 날씨를 검색해오는 작업을 수행합니다. 내부에서 fetch 함수를 사용하고 있으며 수행 결과를 promise를 사용해 json으로 변환한 뒤 결과를 data에 저장하는 작업을 수행합니다.
  • setResult는 입력받은 결과 값을 앞서 정의한 weather에 저장하는 역할을 수행합니다.
  • dateBuilder는 현재 시간을 보기 좋게 만들어주는 역할을 수행합니다.

 

다음으로 스크립트를 사용할 수 있도록 탬플릿을 수정해 줍시다. App.vue의 template영역을 다음과 같이 수정합니다.

 

<template>
  <div id="app">
    <main>
      <div class="search-box">
        <input
          type="text"
          class="search-bar"
          placeholder="Search..."
          v-model="query"
          @keypress="fetchWeather"
        />
      </div>
      <div class="weather-wrap" v-if="typeof weather.main != 'undefined'">
        <div class="location-box">
          <div class="location">{{weather.name}}, {{weather.sys.country}}</div>
          <div class="date">{{dateBuilder()}}</div>
        </div>
        <div class="weather-box">
          <div class="temp">{{weather.main.temp}}℃</div>
          <div class="weather">{{weather.weather[0].main}}</div>
        </div>
      </div>
    </main>
  </div>
</template>

 

새로 추가된 속성에 대해 차근차근 알아봅시다.

  • v-model은 해당 input에 입력된 값을 앞서 정의한 query에 저장해 주는 역할을 합니다. 
  • @keyprss는 해당 input에서 키가 입력될 때마다 fetchWeather함수를 실행시킵니다.
  • v-if는 if문의 역할을 합니다. weather.main이 'undefined'가 아닐 경우에만 해당 div를 표시합니다.
  • {{}}는 data에서 정의한 변수와 methods에 정의한 함수를 사용할 수 있도록 해줍니다.

 

이제 다시 페이지를 새로고침 후 seoul을 검색해 보세요.

 

 

위와 같이 표시되면 정상입니다. 그런데 기억하고 계신가요? 우리는 앞서 warm-bg.jpg도 같이 다운로드하였습니다. 위의 결과인 27.7℃는 충분히 따뜻한 온도이므로 배경화면을 바꿔서 표시해 주는 것이 좋아 보입니다.

 

앞서 설명한 v-if를 사용하면 됩니다. 마지막으로 코드를 다음과 같이 수정해 줍시다.

 

<template>
  <div
    id="app"
    :class="typeof weather.main !='undefined' && Math.round(weather.main.temp) > 16 ? 'warm' : ''"
  >
    <main>
      <div class="search-box">
        <input
          type="text"
          class="search-bar"
          placeholder="Search..."
          v-model="query"
          @keypress="fetchWeather"
        />
      </div>
      <div class="weather-wrap" v-if="typeof weather.main != 'undefined'">
        <div class="location-box">
          <div class="location">{{weather.name}}, {{weather.sys.country}}</div>
          <div class="date">{{dateBuilder()}}</div>
        </div>
        <div class="weather-box">
          <div class="temp">{{Math.round(weather.main.temp)}}℃</div>
          <div class="weather">{{weather.weather[0].main}}</div>
        </div>
      </div>
    </main>
  </div>
</template>

<script>
export default {
  data: function () {
    return {
      api_key: "YOUR_API_KEY_HERE",
      url_base: "https://api.openweathermap.org/data/2.5/",
      query: "",
      weather: {},
    };
  },
  methods: {
    fetchWeather: function (e) {
      if (e.key == "Enter") {
        let fetchUrl = `${this.url_base}weather?q=${this.query}&units=metric&APPID=${this.api_key}`;
        fetch(fetchUrl)
          .then((res) => {
            console.log(res);
            return res.json();
          })
          .then((results) => {
            return this.setResult(results);
          });
      }
    },
    setResult: function (results) {
      this.weather = results;
    },
    dateBuilder: function () {
      let d = new Date();
      let months = [
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December",
      ];
      let days = [
        "Sunday",
        "Monday",
        "Tuesday",
        "Wednesday",
        "Thursday",
        "Friday",
        "Saturday",
      ];
      let day = days[d.getDay()];
      let date = d.getDate();
      let month = months[d.getMonth()];
      let year = d.getFullYear();
      return `${day} ${date} ${month} ${year}`;
    },
  },
};
</script>

<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
body {
  font-family: "montserrat", sans-serif;
}
#app {
  background-image: url("./assets/cold-bg.jpg");
  background-size: cover;
  background-position: bottom;
  transition: 0.4s;
}
#app.warm {
  background-image: url("./assets/warm-bg.jpg");
}
main {
  min-height: 100vh;
  padding: 25px;
  background-image: linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0.25),
    rgba(0, 0, 0, 0.75)
  );
}
.search-box {
  width: 100%;
  margin-bottom: 30px;
}
.search-box .search-bar {
  display: block;
  width: 100%;
  padding: 15px;

  color: #313131;
  font-size: 20px;
  appearance: none;
  border: none;
  outline: none;
  background: none;
  box-shadow: 0px 0px 8px rgba(0, 0, 0, 0.25);
  background-color: rgba(255, 255, 255, 0.5);
  border-radius: 0px 16px 0px 16px;
  transition: 0.4s;
}
.search-box .search-bar:focus {
  box-shadow: 0px 0px 16px rgba(0, 0, 0, 0.25);
  background-color: rgba(255, 255, 255, 0.75);
  border-radius: 16px 0px 16px 0px;
}
.location-box .location {
  color: #fff;
  font-size: 32px;
  font-weight: 500;
  text-align: center;
  text-shadow: 1px 3px rgba(0, 0, 0, 0.25);
}
.location-box .date {
  color: #fff;
  font-size: 20px;
  font-weight: 300;
  font-style: italic;
  text-align: center;
}
.weather-box {
  text-align: center;
}
.weather-box .temp {
  display: inline-block;
  padding: 10px 25px;
  color: #fff;
  font-size: 102px;
  font-weight: 900;
  text-shadow: 3px 6px rgba(0, 0, 0, 0.25);
  background-color: rgba(255, 255, 255, 0.25);
  border-radius: 16px;
  margin: 30px 0px;
  box-shadow: 3px 6px rgba(0, 0, 0, 0.25);
}
.weather-box .weather {
  color: #fff;
  font-size: 48px;
  font-weight: 700;
  font-style: italic;
  text-shadow: 3px 6px rgba(0, 0, 0, 0.25);
}
</style>

 

완성된 코드입니다. 탬플릿의 #app에 v-if를 사용해 온도가 16℃보다 높은 경우 .warm 클래스를 추가하도록 했습니다. 추가된 클래스에 따라 백그라운드가 변경되도록 #app.warm에 대해 background-image가 변경되도록 스타일을 변경하였습니다. 또한 기존 소수점까지 표시되는 기온을 Math.round를 사용해 정수만 표시되도록 수정했습니다.

 

페이지에서 다시 서울을 검색해 보도록 하겠습니다.

 

 

이제 서울을 검색하니 백그라운드 이미지가 따뜻한 이미지로 변경되었습니다. 혹시 모르니 추운 도시를 검색해 봅시다. 남반구는 8월 지금 겨울입니다. 시드니를 검색해 보세요.

 

 

백그라운드 이미지가 정상적으로 변경되어 출력되는 것을 확인할 수 있습니다.

 

 

 

 

 

반응형

+ Recent posts