Git Product home page Git Product logo

l5code's Introduction

이 문서를 확인해주세요: 2022년에 빨간책을 보시는 분들을 위한 실습 시작 가이드


Build Status

라라벨로 배우는 실전 PHP 웹 프로그래밍 (출판용 소스코드)

이 소스코드는 다음 버전을 기준으로 한다.

컴포넌트 버전
프레임워크(laravel/laravel) 5.3.0
핵심 컴포넌트(laravel/framework) 5.3.9

1. 다운로드

myapp은 소스코드를 복제할 디렉터리 이름이다.

~ $ git clone [email protected]:appkr/l5code.git myapp

- OR -

~ $ git clone https://github.com/appkr/l5code.git myapp

- OR -

GUI 환경을 선호하는 독자는 깃허브 데스크톱 프로그램을 이용할 수 있다.

2. 태그 이동

각 챕터(chapter, 章)마다 깃 버전 관리 시스템으로 커밋(commit) 메시지와 태그(tag)를 부여해두었다. 다음 콘솔 명령으로 원하는 챕터의 코드로 이동할 수 있다.

~/myapp(master) $ git tag                # 전체 태그 목록을 확인한다.
~/myapp(master) $ git checkout 1001      # 까지 입력한 후 'Tab' 키를 누르고 'Enter'를 친다.
~/myapp(4443..) $ composer dump-autoload # 오토로드 레지스트리 업데이트

3. 소스코드 구동 준비 하기

복제한 소스코드를 작동을 확인하려면 다음 설명을 따른다. 이 절의 내용은 소스코드를 복제 받고 처음 한번만 하면 된다.

3.1. 프로젝트의 의존성 설치

내려 받은 소스코드는 이 프로젝트가 의존하는 컴포넌트들을 포함하지 않고 있다. 컴포저(composer)로 이 프로젝트가 의존하는 컴포넌트를 설치한다.

소스코드를 복제한 myapp 디렉터리로 이동한다.

~ $ cd myapp

(선택 사항) 태그로 이동했을 때를 대비해서, 마스터 브랜치로 이동한다.

~/myapp $ git checkout master

이 프로젝트가 의존하는 컴포넌트를 설치한다.

~/myapp $ composer install

3.2. 환경 설정

.env.example 파일을 복사하여 .env 파일을 만든다. 파일을 열어 자신의 환경에 맞게 적절히 수정하고 저장한다. 예를 들어 사용할 데이터베이스가 myapp라면 DB_DATABASE=myapp으로 수정한다.

~ $ cd myapp
~/myapp $ cp .env.example .env

암호화 키를 만든다. 아래 명령을 수행함과 동시에 .env 파일에 방금 만든 키가 자동으로 등록된다.

~/myapp $ php artisan key:generate

Mac 또는 Linux를 사용한다면, storage, bootstrap/cache, public\files 디렉터리의 권한을 변경한다.

~/myapp $ chmod -R 777 storage bootstrap/cache public/files

3.3. 데이터베이스 마이그레이션 및 시딩

아래 명령을 실행하기 전에 .env 파일에서 선언한 데이터베이스가 있고, 데이터베이스에 로그인할 사용자가 권한이 있는 지 확인한다. 잘 모르겠다면 7장까지 읽고 다시 이 문서로 돌아오기 바란다.

이 명령은 테이블을 만들고 더미 데이터를 심는 과정이다.

~/myapp $ php artisan migrate --seed --force

3.4. (선택 사항) 마크다운 뷰어용 데이터 파일 설치

마크다운 뷰어를 만드는 실전 프로젝트에서 라라벨 공식 문서를 데이터로 이용했다.

~/myapp $ git clone [email protected]:laravel/docs.git
# - OR -
~/myapp $ git clone https://github.com/laravel/docs.git

4. 소스코드 구동 확인

PHP 내장 웹 서버와 로컬 데이터베이스를 사용한다고 가정한다.

4.1. 브라우저

PHP 내장 웹 서버를 구동한다.

~/myapp $ php artisan serve
# Laravel development server started on http://localhost:8000/

이제 웹 브라우저에서 http://localhost:8000으로 접속해서 실습 예제의 동작을 확인한다.

4.2. 통합 테스트

테스트에 사용할 SQLite 데이터베이스를 만든다.

~/myapp $ touch tests/database.sqlite

테이블을 만들고 시딩한다.

~/myapp $ php artisan migrate --seed --database=testing

PHPUnit을 실행한다.

~/myapp $ vendor/bin/phpunit

5. 라이선스

이 소스코드는 MIT 라이선스를 따른다.

l5code's People

Contributors

appkr avatar jhylmb avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

l5code's Issues

개발 환경에서 .dev 도메인은 더 이상 사용할 수 없습니다.

구글이 .dev 도메인을 구입해서 TLD(Top Level Domain)으로 등록함에 따라, 예제에서 사용한 myapp.dev, api.myapp.dev는 개발 환경에서 사용할 수 없습니다.

따라서 책에서 myapp.dev, myapp.local이라 언급된 부분은, myapp.local, api.myapp.local로 바꿔서 사용해주세요.

영향받는 페이지는 아래와 같습니다.

  • 209 코드 22-1
  • 240 코드 24-3
  • 375 API 응답 예제 부분
  • 378 코드 32-1
  • 382 "로컬 서버를 시동할 땐 api.호스트_이름 없이 해 도 괜찮다($ php artisan serve --host=myapp.local)."
  • 390 코드 33-1
  • 397 코드 33-2
  • 398 HTTP 33-3
  • 401 HTTP 33-4
  • 402 HTTP 33-5
  • 412 HTTP 34-1, HTTP 34-2
  • 416 HTTP 34-3
  • 427 36.1.1 동일 출처 보안 정책 경험하기

데이터베이스 마이그레이션 및 시딩 과정에서 에러 발생

안녕하세요.

책에 있는 과제를 따라하고 설치하는 과정에서 다음과 같은 오류가 발생합니다.
일단 오류를 무시하고 진행해도, 실행은 문제 없습니다만, 해당 오류가 발생되는 이유와, 해결 방안이 있는지요?

환경은 MAC Sierra 이며,
3.3 데이터베이스 마이그레이션 및 시딩 부분입니다

오류 메시지

CoCo-MacBook-Pro:books coco$ php artisan migrate --seed --force
Nothing to migrate.
Seeded: tags table
Seeded: UsersTableSeeder
Seeded: ArticlesTableSeeder
Seeded: article_tag table
Downloading 10 images from lorempixel. It takes time...
File saved: f7c9cc204ba92130b9e691b0df0e659f.jpg
File saved: d9a61a01f10b5bba8984c656260efdad.jpg
File saved: 0ee757598698325be09097a43bcd5555.jpg
File saved: d89121b6f92ee37b0f79971334f80cd4.jpg
File saved: 5ce2fd51c17741bb5fe3a0d4c1a67bb4.jpg
File saved: 1282d7c33267f150db270e3031db87f5.jpg

[ErrorException]
finfo_file(): Empty filename or path

"27장 27.2 UI 개선" 버그 수정

이 문제점은 신모범님께서 제보해 주셨습니다. 고맙습니다.

"포럼 > 글 쓰기" 페이지에서 첨부파일을 먼저 업로드한 후, 글을 전송할 때 Article 모델과 Attachment 모델을 연결하는 로직이 누락되었습니다.

부연하면, Dropzone UI를 이용해서 Ajax로 파일을 업로드하면, AttachmentsControllerattachments 테이블에 업로드된 첨부 파일들의 메타 데이터가 저장하고, 저장 결과를 JSON으로 반환합니다. Dropzone은 JSON을 받아서 이미지면 본문에 이미지를 삽입한 후, attachments라는 숨김 필드에 서버에 이미 생성된 Attachment 모델들의 id 목록을 추가합니다. 이제 attachments 숨김 필드가 있는 상태에서 글을 전송하면 서버에서 Article 모델을 생성합니다. 이 때 이미 업로드된 AttachmentArticle을 연결해야 하는데, 해당 로직이 누락되었습니다.

변경 내용은 다음 링크에서 확인할 수 있습니다.

54fdaf4

1

1

OS X에서 MySQL을 잘못 설치했을 때 다시 설치하는 방법

홈브루로 로컬 OSX 컴퓨터에 설치한 MySQL 데이터베이스를 재설치하는 방법입니다.
479 페이지의 MySQL 설치 후 mysql_secure_installation을 실행하는 부분의 주의사항을 읽지 못하신 독자분들이 있는 것 같습니다.
중요함을 좀 더 강하게 썼어야 하는데 제 불찰입니다.

N을 눌러야 하는 질문에 Y를 눌렀다면, 실습에서 사용할 homestead 사용자에게 secret와 같은 짧은 비밀번호를 부여할 수 없습니다. 이 때는 MySQL을 처음 부터 다시 설치하는 것이 깔끔합니다. 아래 가이드를 따라 주세요.


실행 중인 홈브루 서비스를 확인합니다.

~ $ brew services list
# Name    Status  User  Plist
# dnsmasq started root  /Library/LaunchDaemons/homebrew.mxcl.dnsmasq.plist
# mysql   started appkr /Users/appkr/Library/LaunchAgents/homebrew.mxcl.mysql.plist
# php70   stopped

기존에 사용하던 MySQL 데이터 디렉터리를 확인합니다.

~ $ ls -al /usr/local/var/mysql
# total 185M
# drwxr-xr-x  23 appkr admin  782  1  7 18:18 ./
# drwxrwxr-x   7 appkr admin  238 11 14 10:07 ../
# drwxr-x---  32 appkr admin 1.1K 11 20 21:34 myapp/
# drwxr-x---  77 appkr admin 2.6K 11 14 10:07 mysql/
# drwxr-x---  90 appkr admin 3.0K 11 14 10:07 performance_schema/
# drwxr-x--- 108 appkr admin 3.6K 11 14 10:07 sys/
# ...

MySQL 서버를 중지시키고, 기존에 사용하던 데이터 디렉터리와 MySQL 바이너리를 삭제합니다.

만일의 사태에 대비해 삭제하기 전에 데이터를 백업해 두는 것이 좋습니다.

~ $ mysqldump -uroot -p source__database_name > dump_file_name.sql

~ $ brew services stop mysql
# ==> Successfully stopped `mysql` (label: homebrew.mxcl.mysql)

~ $ rm -rf /usr/local/var/mysql

~ $ brew uninstall mysql
# Uninstalling /usr/local/Cellar/mysql/5.7.16... (13,511 files, 439M)

잘 삭제되었는 지 확인해 볼까요?

~ $ brew services list
# Name    Status  User Plist
# dnsmasq started root /Library/LaunchDaemons/homebrew.mxcl.dnsmasq.plist
# php70   stopped

~ $ brew list | grep mysql

완전히 지워졌으니, 이제 다시 설치를 합니다.

~ $ brew install mysql
# ==> Downloading https://homebrew.bintray.com/bottles/mysql-5.7.17.sierra.bottle.tar.gz
# ######################################################################## 100.0%
# ==> Pouring mysql-5.7.17.sierra.bottle.tar.gz
# ==> Using the sandbox
# ==> /usr/local/Cellar/mysql/5.7.17/bin/mysqld --initialize-insecure --user=appkr --basedir=/usr/local/Cellar/mysql/5.7.17 --datadir=/usr/local/var/mysql --tmp
# ==> Caveats
# We've installed your MySQL database without a root password. To secure it run:
#     mysql_secure_installation
# 
# To connect run:
#     mysql -uroot
# 
# To have launchd start mysql now and restart at login:
#   brew services start mysql
# Or, if you don't want/need a background service you can just run:
#   mysql.server start
# ==> Summary
# 🍺  /usr/local/Cellar/mysql/5.7.17: 14,226 files, 444.4M

MySQL 서비스를 시작하고 접속해 봅니다. 현재는 root 사용자의 비밀번호가 없는 상태입니다.

~ $ brew services start mysql
# ==> Successfully started `mysql` (label: homebrew.mxcl.mysql)

~ $ mysql -uroot
# Welcome to the MySQL monitor.  Commands end with ; or \g.
# Your MySQL connection id is 3
# Server version: 5.7.17 Homebrew
# 
# Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
# 
# Oracle is a registered trademark of Oracle Corporation and/or its
# affiliates. Other names may be trademarks of their respective
# owners.
# 
# Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
# 
# mysql> exit
# Bye

사실 개발 머신에서는 이 상태로 사용해도 아무런 문제가 없습니다. 그럼에도 불구하고 마음의 평화를 위해, 또 편의상 책에서 사용한 homestead 사용자와 같은 비밀번호를 사용하기 위해 root 사용자의 비밀번호를 셋팅해 보겠습니다.

MySQL 쿼리 사용이 익숙하지 않은 사용자들을 위해 mysql_secure_installation라는 도우미 함수를 제공합니다 . 5.7부터라고 알고 있고, 이전 버전에서는 mysql_easy_install(?)였던 걸로 기억합니다.

그냥 계속 엔터를 누르면 VALIDATE PASSWORD PLUGIN이라는 녀석이 설치되는데요. 이 녀석이 활성화되면 우리가 의도한 secret와 같이 짧고 단순한 비밀번호는 쓸 수 없습니다.

사용자의 실수를 방지하기 위해서 이 블록의 콘솔 출력 결과만 한글로 번역합니다. 아래 블록에서 한글로 번역하지 않은 인터렉티브 질문에 대해서는 그냥 엔터를 눌러도 무방합니다.

다시 요약하자면, 실습 중에 root, homstead 사용자에 대해 복잡한 긴 비밀번호를 써도 무방하다면 이 가이드를 따를 필요가 없습니다.

~ $ mysql_secure_installation

# MySQL server의 안전 장치를 활성화합니다.
#
# MySQL에 비밀번호 없이 접속합니다.
#
# 보안을 향상시키기 위해 VALIDATE PASSWORD PLUGIN을 활성화합니다.
# 이 플러그인은 사용자가 강력한 비밀번호를 사용하도록 강제합니다.
# 이 플러그인을 활성화하겠습니까?
#
# 활성화하려면 Y 키를, 그렇지 않으려면 아무키나 누르세요: N
#
# 이제 root 사용자의 비밀번호를 입력해 주세요.
# 새 비밀번호: ******
# 한 번 더 입력해 주세요: ******
#
# By default, a MySQL installation has an anonymous user,
# allowing anyone to log into MySQL without having to have
# a user account created for them. This is intended only for
# testing, and to make the installation go a bit smoother.
# You should remove them before moving into a production
# environment.
# 
# Remove anonymous users? (Press y|Y for Yes, any other key for No) : Y
# Success.
# 
# Normally, root should only be allowed to connect from
# 'localhost'. This ensures that someone cannot guess at
# the root password from the network.
# 
# Disallow root login remotely? (Press y|Y for Yes, any other key for No) : Y
# Success.
# 
# By default, MySQL comes with a database named 'test' that
# anyone can access. This is also intended only for testing,
# and should be removed before moving into a production
# environment.
# 
# 
# Remove test database and access to it? (Press y|Y for Yes, any other key for No) : Y
#  - Dropping test database...
# Success.
# 
#  - Removing privileges on test database...
# Success.
# 
# Reloading the privilege tables will ensure that all changes
# made so far will take effect immediately.
# 
# Reload privilege tables now? (Press y|Y for Yes, any other key for No) : Y
# Success.
# 
# All done!

이제 root 사용자의 비밀번호가 설정되었습니다. 실험해 볼까요?

~ $ mysql -uroot
# ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)

~ $ mysql -uroot -p
# Enter password: ******
# Welcome to the MySQL monitor.  Commands end with ; or \g.
# Your MySQL connection id is 9
# Server version: 5.7.17 Homebrew
# 
# Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
# 
# Oracle is a registered trademark of Oracle Corporation and/or its
# affiliates. Other names may be trademarks of their respective
# owners.
# 
# Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
# 
# mysql> exit
# Bye

백업해 놓은 데이터베이스를 복원하려면 다음 명령을 수행하면 됩니다.

~ $ mysql -uroot -p target_table_name < dump_file_name.sql

고급 사용자라면 mysql_secure_installation대신 다음 쿼리를 직접 수행하면 더 편리합니다.

~ $ mysql -uroot
# Welcome to the MySQL monitor.  Commands end with ; or \g.
# Your MySQL connection id is 10
# Server version: 5.7.17 Homebrew
# 
# Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
# 
# Oracle is a registered trademark of Oracle Corporation and/or its
# affiliates. Other names may be trademarks of their respective
# owners.
# 
# Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
# 
mysql> use mysql;
# Reading table information for completion of table and column names
# You can turn off this feature to get a quicker startup with -A
# 
# Database changed
mysql> SELECT Host, User, authentication_string FROM user;
# +-----------+-----------+-------------------------------------------+
# | Host      | User      | authentication_string                     |
# +-----------+-----------+-------------------------------------------+
# | localhost | root      | *14E65567ABDB5135D0CFD9A70B3032C179A49EE7 |
# | localhost | mysql.sys | *THISISNOTAVALIDPASSWORDTHATCANBEUSEDHERE |
# +-----------+-----------+-------------------------------------------+
# 2 rows in set (0.00 sec)
# 
mysql> UPDATE user SET authentication_string=PASSWORD('new_password') WHERE Host = 'localhost' AND User = 'root';
# Query OK, 1 row affected, 1 warning (0.00 sec)
# Rows matched: 1  Changed: 1  Warnings: 1
# 
mysql> FLUSH PRIVILEGES;
# Query OK, 0 rows affected (0.00 sec)
# 
mysql> exit
# Bye

2022년에 빨간책을 보시는 분들을 위한 실습 시작 가이드

2015년에서 2016년으로 넘어갈 즈음에 라라벨 5.2/PHP7.0으로 온라인에 글을 쓰기 시작했고, 2016년 말 종이 책을 출간할 때는 라라벨 5.3/PHP7.0 기준으로 썼습니다. 6년이 지났습니다. 그간에 저의 빨간책을 사랑해 주신 독자 여러분께 진심을 담아 감사 인사 드립니다.

오늘 어느 독자의 질문을 받고, 오랫만에 PHP와 라라벨 생태계를 살펴보니, PHP는 8.0이고 라라벨은 9.x이군요 !!

현재의 런타임 환경으로 제 책을 보고 따라하신다면 정상 작동하는 코드가 아마도 거의 없을 겁니다. 그럼에도 불구하고, PHP와 라라벨 기초 문법이 바뀐것은 아니므로, 라라벨5.3/PHP7.1 런타임 환경만 갖춘다면 책을 보시는데 지장이 없다고 확신합니다.

2022년에 PHP7.1을 직접 설치할 수 있는 방법은 찾기 힘들겁니다. 책의 예제 코드 중 일부가 PHP7.2 버전부터는 호환되지 않습니다.

아래에서 Docker를 이용해서 PHP7.1 개발 환경을 구성하는 방법을 설명합니다.

요약 버전

이하 명령어는 모두 Mac 기준입니다. 윈도 환경에서의 작동은 어느 독자님의 환경에서 검증했습니다.

~ $ wget https://github.com/appkr/l5code/files/8481555/myapp.zip
~ $ unzip myapp.zip
~ $ cd myapp
~/myapp $ docker-compose  up -d
~/myapp $ docker exec -it laravel cp /app/.env.example /app/.env
~/myapp $ docker exec -it laravel php /app/artisan key:generate
~/myapp $ open http://localhost:8000

애플리케이션 설치

책에서 언급한 laravel new ... 또는 composer create-project ... 명령을 이용하지 않고, 이미 만든 프로젝트의 압축파일을 다운로드 받아 압축을 풉니다. 혹시 모를 의존 충돌을 피하기 위해 vendor 폴더까지 압축에 포함했습니다

myapp.zip

Docker 설치

Docker를 설치하고 실행해주세요

docker-compose

Docker가 설치되고 구동되면, docker-compose 명령을 이용할 수 있습니다. docker-compose 명령으로 MySQL, 웹서버/PHP 런타임등을 한번에 구동합니다.

최초 실행이라면, MySQL에 myapp이란 데이터베이스 스키마와 homestead 사용자를 자동으로 만들어줍니다

이 명령을 실행하기 전에 3306, 6379, 8000 번 포트를 사용하는 프로세스를 중단해야 합니다

~/myapp $ docker-compose  up -d
# up: 시작하겠다는 의미
# -d: detach(백그라운드) 모드로 실행
# 최초 한 번은 시간이 걸립니다

클러스터에 실행중인 컨테이너를 확인합니다. 총 세 개가 나와야 합니다

~/myapp $ docker-compose ps
#  Name                Command                  State                     Ports
# -------------------------------------------------------------------------------------------
# laravel   docker-php-entrypoint apac ...   Up (healthy)   0.0.0.0:8000->80/tcp
# mysql     docker-entrypoint.sh mysql ...   Up (healthy)   0.0.0.0:3306->3306/tcp, 33060/tcp
# redis     docker-entrypoint.sh redis ...   Up (healthy)   0.0.0.0:6379->6379/tcp
  • laravel: Apache2.4/PHP7.1 런타임을 탑재하여 라라벨 애플리케이션을 구동하는 컨테이너
  • mysql: MySQL 5.7.32 서버
  • redis: 레디스 서버; 실습에 필요하지않았던 걸로 기억합니다..

실행중인 컨테이너를 전부 중단하고 재시작하는 방법은

~/myapp $ docker-compose stop
~/myapp $ docker-compose start
~/myapp $ docker-compose restart

문제가 발생해서 컨테이너를 완전히 버리고, 새 컨테이너를 띄우고 싶다면

~/myapp $ docker-compose down
~/myapp $ docker-compose up -d

다만, 컨테이너를 전부 재시작하면 기존에 MySQL에 저장했던 내용은 날아갑니다. 재시작해도 기존 상태를 계속 유지하고 싶다면, docker-compose.yml 파일의 volumes 선언문의 주석을 제거한 후, 클러스터를 재시작해주세요.

커맨드 실행

로컬 개발 서버로 로컬 컴퓨터에서 개발할 때 이렇게 명령하던 것을

~/myapp $ php artisan

Docker 컨테이너를 사용할 때는 이렇게 하셔야 합니다

~/myapp $ docker exec -it laravel php /app/artisan
# Laravel Framework version 5.3.31
# ...

# 또는 Docker 컨테이너에 들어가서 커맨드해도 됩니다
# ~/myapp $ docker exec -it laravel bash
# /myapp $ php artisan

애플리케이션 초기 설정

.env를 복제합니다

~/myapp $ docker exec -it laravel cp /app/.env.example /app/.env

APP_KEY를 만듭니다

~/myapp $ docker exec -it laravel php /app/artisan key:generate

DB 접속 테스트 한번 해볼까요?

~/myapp $ docker exec -it laravel php /app/artisan tinker
# Psy Shell v0.8.18 (PHP 7.1.33 — cli) by Justin Hileman
# New version is available (current: v0.8.18, latest: v0.10.5)
# >>> DB::select('SELECT 1');
# => [
#      {#710
#        +"1": 1,
#      },
#    ]

HTTP 통신 테스트 (웹 브라우저에서 http://localhost:8000 하시면 됩니다)

$ curl -s -I http://localhost:8000
# HTTP/1.1 200 OK
# Server: nginx
# ...

코드 작성 및 디버깅

Docker 컨테이너를 사용한다고 코드 작성 방법이 달라지지 않습니다. 사용하던 코드 에디터로 코드 작성하시면 됩니다.

~/myapp $ pstorm . # phpstorm
~/myapp $ subl .   # sublime 
~/myapp $ code .   # vscode

myapp 프로젝트 폴더는 laravel 컨테이너의 /app폴더로 마운트되어 컨테이너 안에서 실행됩니다. 따라서 컨테이너 안에서 PHP 런타임이 storage/logs/laravel.log에 쓴 내용도 로컬 컴퓨터에서 확인 가능합니다

~/myapp $ tail -f storage/logs/laravel.log

asciicast

[Windows] fileinfo is missing from your system

이 문제점은 님이 제보해주셨습니다. 고맙습니다.


문제점

다음은 Windows 환경에서 fileinfo PHP 확장 모듈이 없어서 발생하는 문제입니다.

appkr@PC MINGW64 ~/l5code (master)
$ composer install
# Loading composer repositories with package information
# Installing dependencies (including require-dev) from lock file
# Your requirements could not be resolved to an installable set of packages.
# 
#   Problem 1
#     - Installation request for intervention/image 2.3.8 -> satisfiable by intervention/image[2.3.8].
#     - intervention/image 2.3.8 requires ext-fileinfo * -> the requested PHP extension fileinfo is missing from your system.
# 
#   To enable extensions, verify that they are enabled in your .ini files:
#     - C:\Bitnami\wampstack-7.0.14-1\php\php.ini
#   You can also run `php --ini` inside terminal to see which files are used by PHP in CLI mode.

증상

  • composer install 할 때 오류가 발생합니다.
  • php artisan serve 할 때 오류가 발생합니다.
  • 책의 예제 코드를 다운로드 받아서 실행할 수 없습니다.

원인

20장에서 설치한 intervention/image 콤포넌트가 fileinfo PHP 확장 모듈에 의존하는데, 이 확장 모듈은 Bitnami Wamp에서 활성화 되어 있지 않습니다 (저도 방금 알았습니다 ㅜㅜ).

// 해당 컴포넌트의 `composer.json`을 살펴보면, `fileinfo` PHP 확장 모듈이 필요함을 알 수 있습니다.
// vendor/intervention/image/composer.json

"require": {
    "php": ">=5.4.0",
    "ext-fileinfo": "*",
    "guzzlehttp/psr7": "~1.1"
},

해결 방법

fileinfo PHP 확장 모듈을 활성화합니다. Bitnami Wamp는 이 모듈을 포함하고 있습니다.

  • C:\Bitnami\wampstack-x.y.z\php\php.ini 파일을 엽니다.
  • ;extension=php_fileinfo.dll로 되어 있는 부분을 찾습니다.
  • extension=php_fileinfo.dll처럼 세미콜론을 제거하고 파일을 저장합니다.
  • Git Bash를 재시작하고, php artisan server가 작동중이라면 재시작합니다.

php ini

다른 APM 패키지를 사용하고 있다면, fileinfo PHP 확장 모듈이 설치되어 있는지 확인하고 위의 설명한대로 php.ini에 해당 모듈을 활성화시켜 줍니다. 해당 모듈이 설치되어 있지 않다면 구글링을 통해 다운로드(또는 컴파일)해서 활성화해야 합니다.

결론

fileinfo PHP 확장 모듈이 활성화되면 모든 기능이 정상 작동합니다.

appkr@PC MINGW64 ~/l5code (master)
$ composer install
# Loading composer repositories with package information
# Installing dependencies (including require-dev) from lock file
# Package operations: 89 installs, 0 updates, 0 removals
#   - Installing squizlabs/php_codesniffer (2.7.0) Downloading: 100%
# 
# ...
# 
#   - Installing symfony/dom-crawler (v3.1.4)
#     Loading from cache
# Generating autoload files
# > Illuminate\Foundation\ComposerScripts::postInstall
# > php artisan optimize
# Generating optimized class loader
appkr@PC MINGW64 ~/l5code (master)
$

고맙습니다.

[OLD & NOT APPLICABLE] 책과 달리 라라벨 5.4, 5.5, 5.6, 5.7, 5.8로 시작하시는 분들은 이 브랜치를 참고해주세요.

이 문서는 유효하지 않습니다. 이슈 목록에서 Docker 문서를 참고해주세요.


Laravel 5.3

책과 같은 라라벨 5.3 버전을 사용하시려면 아래 명령으로 프로젝트를 시작하시면 됩니다.

$ composer create-project "laravel/laravel:5.3" myapp --prefer-dist --verbose

Laravel 5.4, 5.5, 5.6, 5.7

직접 업그레이드해 본 결과, 책에 쓴 예제 코드는 라라벨 5.4, 5.5, 5.6, 5.7에서도 약간의 변경만으로 작동합니다.


Laravel 5.8

5.8로 마이그레이션한 후, UI 및 PHPUnit 테스트는 작동합니다.

그런데, JwtAuth 0.5 버전이 라라벨 5.8에서 작동하지 않은 것으로 확인됩니다. JwtAuth 1.0 버전으로 변경하면 라라벨 5.8과 문제없이 작동하지만, JwtAuth의 API가 변경되어, 변경된 사용법에 맞춰서 예제 코드를 변경해야 합니다.

또, 라라벨 5.8부터 users.id 컬럼이 INTEGER에서 BIG INTEGER로 변경되었습니다. 따라서, users.id 컬럼을 참조하는 스키마 마이그레이션을 아래와 같이 변경해야 합니다. 테렌님이 제보해주셨습니다.

p83. 코드 10-1 database/migrations/TIMESTAMP_create_articles_table.php

 class CreateArticlesTable extends Migration {
     public function up()
         Schema::create('articles', function (Blueprint $table) {
             $table->increments('id');
-            $table->integer('user_id')->unsigned()->index();
+            $table->unsignedBigInteger('user_id')->index();
             // ...

p295. 코드 28-1 database/migrations/TIMESTAMP_create_comments_table.php

 class CreateCommentsTable extends Migration {
     public function up() {
         Schema::create('comments', function (Blueprint $table) { 
             $table->increments('id'); 
-            $table->integer('user_id')->unsigned()->index();
+            $table->unsignedBigInteger('user_id')->index();
             // ...

p310. 코드 28-20 database/migrations/TIMESTAMP_create_votes_table.php

 public function up() {
     Schema::create('votes', function (Blueprint $table) { 
         $table->increments('id'); 
-        $table->integer('user_id')->unsigned();
+        $table->unsignedBigInteger('user_id');
         // ...

php7.0 < 가 필요하지 않을까요?

laravel new myapp

명령으로 라라벨 기본 프로젝트를 생성하고 aws-ubuntu 18.04 LTS 에서

provision.sh
serve.sh

스크립트를 실행하고 라라벨을 돌리면 제대로 작동하지 않습니다.
그래서 일단 프로비저닝 스크립트에 존재하는 php 를 몽땅 7.2로 올려서
재 인스톨한 뒤, nginx 설정을 php7.2 로 바꾸었더니
라라벨이 정상실행 되었습니다.

치명적인 오류는 없었으나, 쉘 스크립트 실행 중
일부 패키지나 파일을 못 찾는 등의 오류가 있었습니다.

composer install 실행 시 프로비저닝으로 구성된 php7.0으로 돌리면
의존성 에러가 발생합니다.

php 버전의 업그레이드가 필요하지 않을까요?

아니면 그냥 composer.json 에서 php 버전을 낮추면 되는건가용?

라라벨 5.4에서 LaravelMix(구 Elixir) 사용

2017/04/29에 내용을 수정했습니다.

  1. version() API는 적용하지 않았습니다.
  2. 빌드 레시피를 변경했습니다.

변경 전에 이 문서를 따라하셨던 분이라면, 아래 두 개의 파일의 변경 사항을 반영해 주세요.

  • resources/views/layouts/app.blade.php
  • webpack.mix.js

라라벨 5.4부터 "Elixir"라는 이름이 컴퓨터 랭귀지 "Elixir"와 헷갈린다는 이유로 "Mix"로 이름이 변경되고, 빌드 시스템도 gulp에서 webpack으로 완전히 변경되었습니다. 라라벨 5.4로 학습하시는 분들은 다음 절차대로 시도해 보시기 바랍니다.

프로젝트를 처음 시작해서 package.json에 아무것도 없는 상태로 가정합니다. 저도 이렇게 해서 테스트했습니다.

~ $ laravel new laravel-mix-test && cd laravel-mix-test
# ...

~/laravel-mix-test $ php artisan --version
# Laravel Framework 5.4.19

Node.js 패키지를 설치하고, 프런트엔드 에셋 빌드가 잘 작동하는 지 확인합니다.

~/laravel-mix-test $ npm install
# ...

~/laravel-mix-test $ npm run dev
# ...

책에서 사용한 Node.js 패키지들을 설치합니다. vuevue-resource는 버전을 지정해야 합니다.

~/laravel-mix-test $ npm install autosize bootstrap-sass dropzone \
font-awesome highlightjs jquery-tabby marked select2 select2-bootstrap-css \
[email protected] [email protected] --save-dev
# ...

설치 후에 package.json 파일에 설치한 버전이 잘 기록되었나 확인합니다.

{
  "private": true,
  "scripts": {
    "dev": "npm run development",
    "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
    "watch-poll": "npm run watch -- --watch-poll",
    "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
    "prod": "npm run production",
    "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
  },
  "devDependencies": {
    "autosize": "^3.0.20",
    "axios": "^0.15.3",
    "bootstrap-sass": "^3.3.7",
    "cross-env": "^3.2.3",
    "dropzone": "^4.3.0",
    "font-awesome": "^4.7.0",
    "highlightjs": "^9.10.0",
    "jquery": "^3.1.1",
    "jquery-tabby": "^0.13.1",
    "laravel-mix": "0.*",
    "lodash": "^4.17.4",
    "marked": "^0.3.6",
    "select2": "^4.0.3",
    "select2-bootstrap-css": "^1.4.6",
    "vue": "^1.0.26",
    "vue-resource": "^0.9.3"
  }
}

webpack.mix.js 파일을 수정합니다.

const { mix } = require('laravel-mix');

mix.sass('resources/assets/sass/app.scss', 'public/css');

mix
  .scripts('node_modules/highlightjs/highlight.pack.js', 'public/js/temp.js')
  .js('resources/assets/js/app.js', 'public/js')
  .scripts([
    'node_modules/highlightjs/highlight.pack.js',
    'public/js/app.js',
    'node_modules/select2/dist/js/select2.js',
    'node_modules/dropzone/dist/dropzone.js',
    'node_modules/marked/lib/marked.js',
    'node_modules/jquery-tabby/jquery.textarea.js',
    'node_modules/autosize/dist/autosize.js',
    'resources/assets/js/forum.js'
  ], 'public/js/app.js');

mix.copy('node_modules/font-awesome/fonts', 'public/fonts');

mix.version() API를 이용한 캐시 버스팅을 이용하지 않으므로, 마스터 레이아웃은 다음과 같은 모양이어야 합니다.

resources/views/layouts/app.blade.php 파일을 수정합니다.

-<link href="{{ elixir('css/app.css') }}" rel="stylesheet">
+<link href="/css/app.css" rel="stylesheet">

-<div class="container">
+<div class="container" id="app"> 
<!--//...--></div>

-<script src="{{ elixir('js/app.js') }}"></script>
+<script src="/js/app.js"></script>

resources/assets/js/app.js 파일을 수정합니다.

require('./bootstrap');
window.Vue = require('vue');

// Vue.component('example', require('./components/Example.vue'));

const app = new Vue({
  el: '#app',

  ready() {
    hljs.initHighlightingOnLoad();
    this.removeFlashMessages();
    this.setJqueryAjaxHeaders();
    this.initBackToTopButton();
  },

  methods: {
    removeFlashMessages() {
      if ($('.alert')) {
        $('.alert').delay(5000).fadeOut();
      }
    },

    setJqueryAjaxHeaders() {
      $.ajaxSetup({
        headers: {
          'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
        }
      });
    },

    initBackToTopButton() {
      $('#back-to-top').on('click', function () {
        $('body,html').animate({
          scrollTop: 0
        }, 800);

        return false;
      });

      $(window).on('scroll', function () {
        var scrollPos = $(window).scrollTop();

        if (scrollPos > 50) {
          $('#back-to-top').fadeIn();
        } else {
          $('#back-to-top').fadeOut();
        }
      });
    }
  }
});

resources/assets/sass/app.scss 파일을 수정합니다.

// Fonts
@import url(https://fonts.googleapis.com/css?family=Raleway:300,400,600);

// Variables
@import "variables";

// Bootstrap
@import "node_modules/bootstrap-sass/assets/stylesheets/bootstrap";

// FontAwesome
@import "node_modules/font-awesome/scss/font-awesome";

// Select2
@import 'node_modules/select2/src/scss/core';

.select2-dropdown,
.select2-container--default .select2-selection--multiple,
.select2-container--default.select2-container--focus .select2-selection--multiple {
  border: 1px solid $input-border;
  border-radius: 4px;
}

.select2-container,
.select2-container--default .select2-selection--multiple {
  font-size: $font-size-base;
}

// Dropzone
@import 'node_modules/dropzone/src/dropzone';

.dropzone {
  border: 1px solid $input-border;
  border-radius: 4px;
}

.dropzone {
  display: none;
}

label[for=my-dropzone]>small {
  cursor: pointer;
}

.preview__content {
  display: none;
  margin-top: $font-size-base * 1.6;
  height: auto;
  @extend .form-control;
  box-shadow: none;
}

// Application Layouts
.container__article{
  padding: 1em 0;
}

.action__article {
  margin-top: 1em;
}

.footer__master {
  margin-top: 1em;

  a {
    display: inline-block;
    color: $text-color;
    text-decoration: none;
    padding: 0 5px;
  }

  ul {
    li {
      padding: 0;

      &:first-child {
        margin-right: 5px;
      }
    }

    li:not(:first-child) {
      a:hover,
      a:active,
      a:focus {
        border-radius: 3px;
        background-color: darken($body-bg, 5%);
        transition: all 0.3s ease;
      }

      &.active > a {
        border-radius: 3px;
        background-color: $body-bg;
      }
    }
  }
}

.back-to-top {
  cursor: pointer;
  position: fixed;
  bottom: 5px;
  right: 5px;
  display: none;
  z-index: 9998;
  transition: all 0.5s ease-in-out;
  padding: 8px 10px;
}

// Sidebar
.sidebar__article,
.tags__article,
.attachments__article {
  a {
    color: $text-color;
    text-decoration: none;
  }
}

.sidebar__article {
  ul {
    list-style: none;
    margin: 0;
    padding: 0;
  }

  li {
    a {
      padding: 0.5rem;
      display: block;
    }

    a:hover,
    a:active,
    a:focus {
      border-radius: 3px;
      background-color: darken($body-bg, 5%);
      transition: all 0.3s ease;
    }

    &.active>a {
      border-radius: 3px;
      background-color: darken($body-bg, 10%);
    }
  }

  .badge {
    float: right;
    margin-right: 1rem;
  }

  form {
    margin-bottom: 30px;
  }
}

// Article
.content__article {
  margin: 2em;
  img {
    display: block;
    height: auto;
    max-width: 100%;
    margin: 0 auto;
    transition: all .2s ease-in-out
  }
}

// Tags Line, Attachments Line
.tags__article,
.attachments__article {
  list-style: none;
  margin: 0;
  padding: 0;
  font-weight: 100;
  font-size: $font-size-base * 0.8;

  li {
    display: inline-block;
  }

  li:first-child {
    margin-right: 10px;
  }

  li:not(:first-child) {
    padding: 4px 8px;
    background-color: darken($body-bg, 5%);
    border-radius: 3px;

    &:hover {
      background-color: darken($body-bg, 10%);
    }
  }
}

// Comments
.link__login__comment {
  padding: 1em;
  margin-bottom: 1em;
  border-radius: 3px;
  border: 1px solid $input-border;
  width: 100%;
}

.item__comment.top {
  padding: 1rem;
  border-radius: 4px;
  position: relative;
  overflow: visible;
  float: right;
  width: 100%;
  border: 1px solid $input-border;
}

.media__edit__comment,
.media__create__comment.sub {
  display: none;
}

.form__new__comment,
.list__comment {
  .form-group {
    width: 100%;
    margin: auto;
  }

  div.text-right {
    margin: 10px auto;
  }
}

.content__comment {
  p {
    margin: 0;
  }
}

.action__comment {
  button, span {
    font-size: $font-size-base * 0.8;
    font-weight: 400;
    text-decoration: none;
  }

  button {
    border: none;
    background: none;
    margin: 0;
    padding: 0;
  }

  button:hover,
  button:active,
  button:focus {
    transition: all .2s ease-in-out
  }

  &>span {
    margin: 0 5px;
  }
}

// Form
.form-control {
  box-shadow: none;
}

.form__auth {
  max-width: 400px;
  width: 100%;
  padding: 15px;
  margin: 0 auto;

  .checkbox {
    margin-bottom: 1rem;
  }

  .checkbox {
    font-weight: normal;
  }

  .form-control {
    position: relative;
    height: auto;
    box-sizing: border-box;
    padding: 0.8rem;
  }

  .form-control:focus {
    z-index: 2;
    border-color: $input-border;
    box-shadow: none;
  }

  input {
    margin-bottom: 10px;
  }

  .login-or {
    position: relative;
    margin-top: 20px;
    margin-bottom: 20px;
    padding-top: 15px;
    padding-bottom: 15px;
  }

  .span-or {
    display: block;
    position: absolute;
    left: 50%;
    top: -1px;
    margin-left: -25px;
    background-color: $body-bg;
    width: 50px;
    text-align: center;
  }

  .hr-or {
    margin-top: 0px !important;
    margin-bottom: 0px !important;
  }

  .fa-github {
    margin-right: 10px;
  }
}

span.form-error {
  margin-top: 5px;
  display: block;
  font-size: 0.8em;
  color: $brand_danger;
  font-style: italic;
  font-weight: 100
}

// Alert, Flash Messages
.alert {
  display: inline-block;
  position: fixed;
  bottom: 50px;
  right: 15px;
  max-width: 450px;
  opacity: .8;
  z-index: 999;
}

// Markdown Viewer
.docs__sidebar {
  margin-top: 20px;
}

#app-layout {
  pre {
    background: #000000;
    border-radius: 0;
  }

  // 이하 데일 리즈의 칼라 스킴에서 복사한 스타일시트

  .hljs {
    display: block;
    padding: 0.5em;
    background: #000000;
    color: #BABABA;
  }

  .hljs-comment,
  .hljs-template_comment,
  .hljs-javadoc {
    color: #494B4D;
  }

  .hljs-keyword,
  .ruby .hljs-function .hljs-keyword,
  .hljs-request,
  .hljs-status,
  .nginx .hljs-title {
    color: #F08D24;
  }

  .hljs-function .hljs-keyword,
  .hljs-sub .hljs-keyword,
  .method,
  .hljs-list .hljs-title {
    color: #68C244;
  }

  .hljs-string,
  .hljs-tag .hljs-value,
  .hljs-cdata,
  .hljs-filter .hljs-argument,
  .hljs-attr_selector,
  .apache .hljs-cbracket,
  .hljs-date,
  .tex .hljs-command,
  .coffeescript .hljs-attribute {
    color: #F2D42C;
  }

  .hljs-subst {
    color: #DAEFA3;
  }

  .hljs-regexp {
    color: #E9C062;
  }

  .hljs-title,
  .hljs-sub .hljs-identifier,
  .hljs-pi,
  .hljs-tag,
  .hljs-tag .hljs-keyword,
  .hljs-decorator,
  .hljs-shebang,
  .hljs-prompt {
    color: #E8341C;
  }

  .hljs-symbol,
  .ruby .hljs-symbol .hljs-string,
  .hljs-number {
    color: #8E69C9;
  }

  .hljs-params,
  .hljs-variable,
  .clojure .hljs-attribute {
    color: #1CC3E8;
  }

  .css .hljs-tag,
  .hljs-rules .hljs-property,
  .hljs-pseudo,
  .tex .hljs-special {
    color: #E8341C;
  }

  .css .hljs-class {
    color: #1CC3E8;
  }

  .hljs-rules .hljs-keyword {
    color: #1CC3E8;
  }

  .hljs-rules .hljs-value {
    color: #E8341C;
  }

  .css .hljs-id {
    color: #8B98AB;
  }

  .hljs-annotation,
  .apache .hljs-sqbracket,
  .nginx .hljs-built_in {
    color: #9B859D;
  }

  .hljs-preprocessor,
  .hljs-pragma {
    color: #8996A8;
  }

  .hljs-hexcolor,
  .css .hljs-value .hljs-number {
    color: #DD7B3B;
  }

  .css .hljs-function {
    color: #DAD085;
  }

  .diff .hljs-header,
  .hljs-chunk,
  .tex .hljs-formula {
    background-color: #0E2231;
    color: #F8F8F8;
    font-style: italic;
  }

  .diff .hljs-change {
    background-color: #4A410D;
    color: #F8F8F8;
  }

  .hljs-addition {
    background-color: #253B22;
    color: #F8F8F8;
  }

  .hljs-deletion {
    background-color: #420E09;
    color: #F8F8F8;
  }

  .coffeescript .javascript,
  .javascript .xml,
  .tex .hljs-formula,
  .xml .javascript,
  .xml .vbscript,
  .xml .css,
  .xml .hljs-cdata {
    opacity: 0.5;
  }

  // 이상 데일 리즈의 칼라 스킴에서 복사한 스타일시트
}

// 미디어 쿼리
@media screen and (max-width: 991px) {
  .action__article {
    margin-bottom: 1em;
  }

  .sidebar__article {
    margin-bottom: 2em;
  }

  .item__comment {
    &>a.pull-left {
      display: none;
    }
  }
}

@media screen and (max-width: 767px) {

}

@media screen and (max-width: 479px) {

}

resources/assets/js/forum.js 파일을 다운로드 합니다.

~/laravel-mix-test $ wget https://raw.githubusercontent.com/appkr/l5code/master/resources/assets/js/forum.js -O resources/assets/js/forum.js

에셋 빌드를 수행합니다.

~/laravel-mix-test $ npm run dev 

laravel-mix

mix

[WIndows] npm install이 안되면 yarn을 이용해 주세요.

라라벨 5.3의 package.json이 의존하는 Node.js 패키지 중에 하나에 대한 의존성이 깨진 것 같습니다.

~/l5code(master) $ npm install 
# npm ERR! git clone C:\Users\suchc\AppData\Roaming\npm-cache\_git-remotes\git# -https-github-com-jeroennoten-webpack-stream-git-patch-1-60be6dae 
# ...
# npm ERR! fatal: '/cygdrive/c/Users/suchc/AppData/Roaming/# npm-cache/_git-remotes/#git-github-com-jeroennoten-webpack-stream-git-patch-1-b24e730a/C:\Users\such c\AppData\Roaming\npm-cache\_git-remotes\git-github-com-jeroennoten-webpack- stream-git-patch-1-b24e730a' does not appear to be a git repository
# npm ERR! fatal: Could not read from remote repository.
# npm ERR!
# npm ERR! Please make sure you have the correct access rights
# npm ERR! and the repository exists.
# npm ERR!
# npm ERR!
# npm ERR! If you need help, you may report this error at:
# npm ERR! <https://github.com/npm/npm/issues>
# 
# npm ERR! Please include the following file with any support request:
# npm ERR! C:\Users\suchc\.babun\cygwin\home\suchc\l5code\npm-debug.lognpm # ERR! Windows_NT 10.0.14393
# npm ERR! argv "C:\\Program Files\\nodejs\\node.exe" "C:\\Program  Files\\nodejs\\node_modules\\npm\\bin\\npm-cli.js" "install" "suchc@homepc"
# npm ERR! node v6.10.1
# npm ERR! npm v3.10.10
# npm ERR! code E404
# 
# npm ERR! 404 Registry returned 404 for GET on https://registry.npmjs.org/# suchc
# npm ERR! 404
# npm ERR! 404 'suchc' is not in the npm registry.
# npm ERR! 404 You should bug the author to publish it (or use the name  yourself!)
# npm ERR! 404
# npm ERR! 404 Note that you can also install from a
# npm ERR! 404 tarball, folder, http url, or git url.
# 
# npm ERR! Please include the following file with any support request:
# npm ERR! C:\Users\suchc\.babun\cygwin\home\suchc\l5code\npm-debug.log

npm 대신 yarn을 사용하여 문제를 해결하시기 바랍니다.

~ $ npm install --global yarn

~ $ yarn --version
# 0.21.3

~ $ cd l5code
~/l5code(master) $ yarn install
# ...
#  | | +-- [email protected]
#  | | `-- [email protected]
#  | `-- [email protected]
#  `-- [email protected]
#  +-- [email protected]
#  | `-- [email protected]
#  `-- [email protected]

~/l5code(master) $ gulp
# 그림으로 첨부합니다.

yarn

이 문제점은 이용준님이 제보해주셨습니다. 고맙습니다.

[OLD & NOT APPLICABLE] 새 프로젝트 생성 및 예제 프로젝트 사용에 어려움이 있는 분들을 위해

이 문서는 유효하지 않습니다. 이슈 목록에서 Docker 문서를 참고해주세요.


프로젝트를 직접 생성하는 경우

  • page #2 ~ 1.1 새로운 라라벨 프로젝트 만들기
$ composer create-project laravel/laravel myapp --prefer-dist --verbose
$ cd myapp
$ php artisan --version
$ php artisan serve

asciicast


예제 프로젝트를 내려 받아 사용하는 경우

  • page #xxv~ III 소스 코드 활용
$ git clone https://github.com/appkr/l5code.git myapp
$ cd myapp
$ composer install
$ cp .env.example .env
$ php artisan key:generate
$ chmod -R 775 storage bootstrap/cache public/files
$ cat .env | grep "DB_DATABASE\|DB_USERNAME\|DB_PASSWORD"
$ php artisan migrate --seed
$ php artisan serve

asciicast

오탈자 신고해 주세요.

책을 읽다가 오탈자를 발견하시면 신고해 주세요. 이 글에 댓글로 써 주세요.

  • 페이지 번호
  • 오탈자 내용

을 써 주시면 됩니다.

혹시라도 이 책을 다시 찍게 된다면, 후배 독자들이 더 완성도 있는 책을 읽을 수 있을 겁니다.
이 저장소는 깃허브가 서비스를 접을 때까지, 삭제하지 않을 것이므로 여러분들의 기여가 영구적으로 보관됩니다.

고맙습니다.

page 29 by @appkr
-ul>li*3>a + Tab을 입력하고 Tab 키를 누르면
+ul>li*3>a를 입력하고 Tab 키를 누르면
page 52 by @mAKEkr
// 코드 7-1
public function down() {
-    Schema::drop('posts'); 
+    Schema::dropIfExists('posts'); 
}
page 53 by @mAKEkr
-Schema::drop(string $table) 테이블을 지운다.
+Schema::dropIfExists(string $table) 테이블을 지운다. 
page 64 by @namo429
// 표 8-1
-ArticlesController@delete
+ArticlesController@destroy
page 64 by @appkr
// 표 8-1 라라벨 5.2까지는 RESTful 리소스 라우트에서 복수형의 라우트 파라미터를 쓰다가 5.3부터 단수로 바뀌었습니다.
-{articles}
+{article}
page 72 by @show0910
// 그림 9-1 웹 브라우저의 마지막 쿠키 기록 부분
-expires=zzz
+expires=aaa
page 89 by @mAKEkr
// 콘솔 10-7
+2번 사용자가 없으면 1번 사용자를 이용한다. App\User::find(1)->articles()->...
>>> App\User::find(2)->articles()->create([ 
... 'title' => 'Second article',
... 'content' => 'Second content',
... ]);
page 101 by @show0910
-코드 12-3을 참고해서 포럼 글 목록을 포시할 뷰를 만들자.
+코드 12-3을 참고해서 포럼 글 목록을 표시할 뷰를 만들자.
page 122 by @mAKEkr 
// 코드 블록 제목
-코드 14-4 app/Http/Listeners/ArticlesEventListener.php
+코드 14-4 app/Listeners/ArticlesEventListener.php
page 123 by @mAKEkr 
// 코드 블록 제목
-코드 14-6 app/Http/Events/ArticleCreated.php
+코드 14-6 app/Events/ArticleCreated.php
page 129 by @dspaudio
-wehreEmail()은 동적 메서드다
+whereEmail()은 동적 메서드다
page 137 by @corean
-string g iven
+string given
page 223 by @namo429
// 코드 블록 타이틀
-코드 23-14 app/Events/ArticleCreated.php
+코드 23-14 app/Events/UserCreated.php

```diff
page 223 by @namo429
// 코드 23-14
-class UserCreated extends Event
+class UserCreated
page 228 by @corean 
-코드 23-21를 참고하여
+코드 23-12와 다음 코드 블록을 참고하여
page 232 by @namo429
// 코드 23-27 비밀번호 변경 로직 누락분 추가
public function postReset(Request $request)
{
    $this->validate($request, [
        'email' => 'required|email|exists:users',
        'password' => 'required|confirmed',
        'token' => 'required'
    ]);

    $token = $request->get('token');

    if (! \DB::table('password_resets')->whereToken($token)->first()) {
        return $this->respondError('URL이 정확하지 않습니다.');
    }

+    \App\User::whereEmail($request->input('email'))->first()->update([
+        'password' => bcrypt($request->input('password'))
+    ]);

    \DB::table('password_resets')->whereToken($token)->delete();

    return $this->respondSuccess(
        '비밀번호를 바꾸었습니다. 새로운 비밀번호로 로그인하세요.'
    );
}
page 233  by @namo429
// 코드 23-29
-class PasswordRemindCreated extends Event
+class PasswordRemindCreated
page 250 by @namo429
// 코드 25-1
-parent::boot($router);
+parent::boot();
page 262 by @dalicom
// 코드 25-18 아래 설명
-$gate->define('update', function ($user, $model) {...})
+Gate::define('update', function ($user, $model) {...})
page 266 by @dalicom
// 코드 25-25 아래 설명
-$gate->before(function ($user, $ability) { ... }
+Gate::before(function ($user, $ability) { ... })
page 285 by @namo429
// 코드 27-13
-{{ $file->filename }} ({{ $file->bytes }})
+{{ $attachment->filename }} ({{ $attachment->bytes }})
page 287 by @namo429
// 코드 블록 제목
-코드 27-17 routes/web.php
+코드 27-17 resources/views/articles/partial/form.blade.php
page 305 by @namo429
// 코드 28-13
-public function boot(Router $router)
+public function boot()
-parent::boot($router);
+parent::boot();
page 327 by by @namo429
// 코드 29-12
-class CommentsEvent extends Event
+class CommentsEvent
page 347 by @namo429
// 코드 블록 제목
-코드 29-40 app/Http/Controller/Cacheable.php
+코드 29-40 app/Http/Controllers/Cacheable.php
// 코드 29-40
-namespace App\Http\Cacheable.php;
+namespace App\Http\Controllers;
page 352 by @dalicom
// 코드 30-4의 파일 경로가 틀렸고, 편집 오류로 2개의 코드 블록으로 분리되어야 합니다.
-resources/lang/ko/comments.php
+resources/lang/ko/forum.php
page 407 by @dalicom
// 코드 34-3
-public function transform(\App\Article $item)
+public function transform(\App\Article $article)
page 497 by @tiher
-PSR-2 표준 권고안은 어디까지나 elsif다
+PSR-2 표준 권고안은 어디까지나 elseif다
page 512 by @nosent79
// 코드 C-13 박스 제목
-Writing.php
+Post.php

이상의 내용은 곧 나올 2쇄에 모두 반영되었습니다.


page 109 by @dalgonafactory 
-유효성 검사 오류가 발생하면 컨트롤러는 lluminate\Support\MessageBag 인스턴스를 만들어
+유효성 검사 오류가 발생하면 컨트롤러는 Illuminate\Support\MessageBag 인스턴스를 만들어
page 237 by @harryhan24
-갓허브
+깃허브

typo_237

page 510 by @luritas
// 코드 C-10
-new Post('Lorem ipsum dolor sit amet')->save();
+(new Post('Lorem ipsum dolor sit amet'))->save();
page 125 by @dalicom
- 우리가 앞에서 이 클래스의 boot() 메서드 본문에 $events->listen()으로 여러 번 ...
+ 우리가 앞에서 이 클래스의 boot() 메서드 본문에 \Event::listen()으로 여러 번 ...

이상의 내용은 3쇄에 모두 반영되었습니다.


page 363 by @YongukMoon
- $timezone = (auth->user->timezone) ?: config('app.timezone');
+$timezone = (auth()->user()->timezone) ?: config('app.timezone'); 

[OLD & NOT APPLICABLE] Mac에서 Homebrew로 개발환경을 준비하시는 분들을 위한 안내

Homebrew에서 [email protected]을 더 이상 받을 수 없습니다. 이슈 목록에서 Docker 문서를 참고해주세요.


Mac에서 Homebrew로 개발환경을 준비하시는 분들을 위한 정오표

Homebrew에서 homebrew/php 탭(저장소)을 운영하지 않고, 해당 탭은 메인 저장소에 병합되어, 브루 탭 추가없이 사용할 수 있습니다.

PHP7.0은 2019년초에 공식적으로 End Of Life되었으므로, PHP7.1을 사용하시기를 권장합니다.

PHP7.2 이상의 버전에서는 count() API가 변경되었고, 책에 수록한 예제코드가 오류룰 일으키므로 PHP7.1 설치 및 사용을 권장합니다


p.477부터 아래 내용을 참고바랍니다.

A.2 Mac

라라벨이 동작하는 데 문제가 없다면 이 책에서 제시하는 것과 다른 도구, 다른 버전을 사용 해도 무방하다.

A.2.1 개발 도구 설치

홈브루

홈브루(Homebrew)는 Mac용 패키지 관리 도구다. 홈브루를 이용해서 PHP나 MySQL 등 실습 에 필요한 다른 패키지를 설치할 것이다.

콘솔 A-1 홈브루 설치 및 설치 확인

# 긴주소타이핑이번거롭다면 'homebrew install'로구글링해서복사하여사용한다.
$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/ install)"
$ brew --version
# Homebrew 2.1.10

PHP

PHP 최신 버전인 7.0을 설치한다. 홈브루로 설치가 가능한지 먼저 확인하자.

콘솔 A-2 설치 가능한 PHP 버전 검색

$ brew search php@
# [email protected]    [email protected]    php

버전이 붙지 않은 php가 PHP 7.3이며, [email protected], [email protected]는 각각 7.1과 7.2버전니다. 예제 코드를 구동하는데 문제가 없는 7.1 버전을 설치하자.

콘솔 A-3 PHP 설치 및 설치 확인

$ brew install [email protected]

$ php --version
# PHP 7.1.29 (cli) (built: May 21 2019 20:05:17) ( NTS )

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.