Skip to main content
Summary

왜 함수가 필요한가

앞 장에서는 배열과 문자열을 다루며 게시글 목록, 검색, 제목 줄이기 같은 작은 기능을 만들어 보았다. 코드를 조금만 길게 작성해도 같은 작업이 여러 번 반복된다. 예를 들어 상품 가격에 부가세를 더하거나, 게시글 제목을 안전하게 출력하거나, 문자열이 너무 길 때 줄이는 코드는 여러 페이지에서 필요할 수 있다.

이때 사용하는 도구가 함수다. 함수는 자주 쓰는 코드를 이름 붙여 묶어 둔 것이다. 필요할 때 함수 이름을 호출하면 묶어 둔 코드가 실행된다.

간단히 말해 함수는 이런 역할을 한다.

  • 반복되는 코드를 줄인다.

  • 코드의 의도를 이름으로 드러낸다.

  • 계산, 출력, 검증 같은 역할을 나누기 쉽게 만든다.

  • 나중에 수정할 때 한 곳만 고치게 해 준다.

함수는 작은 예제 코드를 실제 애플리케이션 구조로 발전시키는 첫 번째 도구다.

함수로 코드를 재사용하기AI 생성 이미지

함수 선언과 호출

PHP에서 함수는 function 키워드로 만든다.

<?php
function sayHello(): void
{
    echo '안녕하세요';
}

sayHello();

function sayHello()는 sayHello라는 이름의 함수를 만든다는 뜻이다. 중괄호 {} 안에는 함수가 실행할 코드를 작성한다. 아래의 sayHello();는 함수를 호출하는 코드다.

함수 이름은 의미 있게 짓는 것이 좋다. a(), test()처럼 모호한 이름보다 calculateTotalPrice(), escapeHtml(), formatDate()처럼 역할이 드러나는 이름이 좋다.

위 예제의 : void는 반환값이 없다는 뜻이다. 반환값은 잠시 뒤에 자세히 다룬다.

매개변수로 값을 전달하기

함수가 항상 같은 일만 한다면 활용도가 낮다. 함수 밖에서 값을 받아 처리하려면 매개변수를 사용한다. 매개변수는 함수가 일을 할 때 필요한 입력값이다.

<?php
function greet(string $name): void
{
    echo $name . '님, 안녕하세요.';
}

greet('지민');
greet('서연');

$name이 매개변수다. greet('지민')을 호출하면 함수 안의 $name에는 '지민'이 들어간다. greet('서연')을 호출하면 $name에는 '서연'이 들어간다.

매개변수를 여러 개 받을 수도 있다.

<?php
function printProduct(string $name, int $price): void
{
    echo $name . ': ' . number_format($price) . '원';
}

printProduct('키보드', 35000);

함수는 필요한 값을 매개변수로 받아야 한다. 함수 안에서 바깥 변수를 몰래 가져다 쓰는 방식은 처음에는 편해 보이지만, 코드가 커질수록 흐름을 따라가기 어려워진다.

반환값으로 결과 돌려주기

함수는 echo로 바로 출력할 수도 있지만, 더 좋은 방식은 계산 결과를 return으로 돌려주는 것이다. return은 함수의 결과를 호출한 곳으로 보내고, 함수 실행을 끝낸다.

<?php
function addVat(int $price): int
{
    return (int) round($price * 1.1);
}

$totalPrice = addVat(10000);
echo number_format($totalPrice) . '원';

이 함수는 가격을 받아 부가세 10%를 더한 값을 반환한다. 함수 안에서는 계산만 하고, 출력은 함수 밖에서 한다. 이렇게 계산 로직과 출력 로직을 나누면 재사용하기 쉽다.

나쁜 예를 먼저 보자.

<?php
function printPriceWithVat(int $price): void
{
    echo number_format((int) round($price * 1.1)) . '원';
}

이 함수는 계산과 출력을 동시에 한다. 화면에 보여줄 때는 괜찮지만, 계산 결과를 데이터베이스에 저장하거나 다른 계산에 다시 사용하기 어렵다.

더 나은 방식은 다음과 같다.

<?php
function calculatePriceWithVat(int $price): int
{
    return (int) round($price * 1.1);
}

function formatWon(int $price): string
{
    return number_format($price) . '원';
}

$priceWithVat = calculatePriceWithVat(10000);
echo formatWon($priceWithVat);

첫 번째 함수는 계산만 한다. 두 번째 함수는 원화 표시 문자열을 만든다. 역할이 나뉘면 함수 이름만 읽어도 코드의 흐름이 보인다.

기본값 매개변수 사용하기

매개변수에는 기본값을 지정할 수 있다. 함수 호출 시 값을 넘기지 않으면 기본값이 사용된다.

<?php
function shorten(string $text, int $length = 20): string
{
    if (mb_strlen($text) <= $length) {
        return $text;
    }

    return mb_substr($text, 0, $length) . '...';
}

echo shorten('PHP 함수로 코드를 재사용하는 방법');
echo shorten('PHP 함수로 코드를 재사용하는 방법', 10);

$length = 20이 기본값 매개변수다. 길이를 따로 지정하지 않으면 20글자를 기준으로 줄인다. 두 번째 호출처럼 값을 넘기면 그 값이 사용된다.

여기서 사용한 mb_strlen()과 mb_substr()은 한글처럼 여러 바이트로 표현되는 문자를 다룰 때 유용하다. 만약 실행 환경에서 이 함수들을 찾을 수 없다는 오류가 난다면 PHP의 mbstring 확장이 활성화되어 있는지 확인해야 한다.

기본값이 있는 매개변수는 보통 뒤쪽에 둔다.

<?php
function createExcerpt(string $text, int $length = 100, string $suffix = '...'): string
{
    if (mb_strlen($text) <= $length) {
        return $text;
    }

    return mb_substr($text, 0, $length) . $suffix;
}

필수 값은 앞에, 선택 값은 뒤에 두면 함수를 호출하는 코드가 자연스럽다.

타입 선언과 반환 타입 선언

PHP는 변수에 들어가는 값에 따라 자료형이 정해지는 동적 타입 언어다. 편리하지만, 숫자를 기대한 함수에 문자열이나 배열이 들어오면 예상하지 못한 결과가 나올 수 있다.

그래서 PHP 8.x에서는 함수에 타입을 적극적으로 적는 습관이 좋다.

<?php
function calculateDiscountPrice(int $price, int $discountRate): int
{
    return (int) round($price * (100 - $discountRate) / 100);
}

여기서 int $price는 $price가 정수여야 한다는 뜻이다. int $discountRate도 정수여야 한다. 함수 이름 뒤의 : int는 이 함수가 정수를 반환한다는 뜻이다.

자주 쓰는 타입은 다음과 같다.

  • int: 정수

  • float: 실수

  • string: 문자열

  • bool: 참 또는 거짓

  • array: 배열

  • void: 반환값 없음

  • mixed: 여러 타입을 받을 수 있음

mixed는 편리하지만 남용하지 않는 것이 좋다. 함수가 어떤 값을 받고 어떤 값을 돌려주는지 알기 어려워지기 때문이다.

더 엄격하게 타입을 확인하고 싶다면 파일 맨 위에 다음 선언을 추가할 수 있다.

<?php
declare(strict_types=1);

function doubleNumber(int $number): int
{
    return $number * 2;
}

declare(strict_types=1);은 스칼라 타입 선언을 더 엄격하게 검사하도록 한다. 예를 들어 정수를 기대하는 매개변수에 문자열 '10'을 넘기는 실수를 더 빨리 발견할 수 있다. 이 선언은 파일의 맨 위, 보통 <?php 바로 다음에 두는 것이 좋다.

한 가지 기억할 점도 있다. strict_types는 "함수가 선언된 파일 전체가 무조건 엄격해진다"기보다는, 그 파일에서 함수를 호출할 때 타입을 엄격하게 다루도록 만드는 설정에 가깝다. 처음에는 조금 까다롭게 느껴질 수 있지만, 실수를 빨리 발견하는 데 도움이 된다.

스코프: 변수의 사용 범위

스코프는 변수를 사용할 수 있는 범위다. 함수 안에서 만든 변수는 기본적으로 함수 안에서만 사용할 수 있다. 이를 지역 변수라고 한다.

<?php
function showMessage(): void
{
    $message = '함수 안에서 만든 변수';
    echo $message;
}

showMessage();
// echo $message; // 오류: 함수 밖에서는 사용할 수 없음

반대로 함수 밖에서 만든 변수도 함수 안에서 자동으로 사용할 수 없다.

<?php
$siteName = 'PHP 교과서';

function printSiteName(): void
{
    // echo $siteName; // 오류
}

함수 안에서 바깥 변수가 필요하다면 매개변수로 전달하는 것이 가장 좋다.

<?php
$siteName = 'PHP 교과서';

function printSiteName(string $name): void
{
    echo $name;
}

printSiteName($siteName);

PHP에는 global 키워드도 있다.

<?php
$count = 0;

function increaseCount(): void
{
    global $count;
    $count++;
}

하지만 global은 가능한 한 피하는 것이 좋다. 함수가 어떤 외부 값에 의존하는지 함수 선언만 보고 알기 어렵기 때문이다. 작은 예제에서는 편해 보여도, 실제 프로젝트에서는 버그의 원인이 되기 쉽다. 필요한 값은 매개변수로 받고, 결과는 반환값으로 돌려주는 습관을 들이자.

익명 함수와 화살표 함수

함수에는 이름이 있는 함수만 있는 것이 아니다. 이름 없이 변수에 담거나 다른 함수에 전달하는 함수도 있다. 이를 익명 함수라고 한다.

배열 처리 함수와 함께 자주 사용한다.

<?php
$prices = [10000, 25000, 39000];

$formattedPrices = array_map(function (int $price): string {
    return number_format($price) . '원';
}, $prices);

print_r($formattedPrices);

array_map()은 배열의 각 값을 변환해 새 배열을 만든다. 여기서는 각 가격을 원화 문자열로 바꾼다.

PHP 7.4부터는 화살표 함수를 사용할 수 있다. 짧은 변환 로직에는 화살표 함수가 더 간결하다.

<?php
$prices = [10000, 25000, 39000];

$formattedPrices = array_map(
    fn (int $price): string => number_format($price) . '원',
    $prices
);

화살표 함수는 fn으로 시작하고, => 뒤에 반환할 값을 적는다. 한 줄짜리 간단한 계산이나 변환에 적합하다.

조건에 맞는 값만 남길 때도 사용할 수 있다.

<?php
$posts = [
    ['title' => 'PHP 기초', 'views' => 150],
    ['title' => '배열 다루기', 'views' => 80],
    ['title' => '함수 사용법', 'views' => 210],
];

$popularPosts = array_filter(
    $posts,
    fn (array $post): bool => $post['views'] >= 100
);

array_filter()는 조건을 만족하는 값만 남긴다. 이때 원래 배열의 키를 유지하므로, 나중에 연속된 숫자 인덱스가 꼭 필요하다면 array_values()로 다시 정리할 수 있다.

익명 함수와 화살표 함수는 강력하지만, 너무 복잡한 로직을 한 줄에 몰아넣으면 읽기 어렵다. 코드가 길어진다면 이름 있는 함수로 분리하는 편이 낫다.

안전한 출력 함수를 만들기

웹 페이지에서는 사용자 입력이나 데이터베이스에서 가져온 값을 그대로 출력하면 위험할 수 있다. HTML로 해석될 수 있는 문자를 안전한 형태로 바꿔야 한다. 앞 장들에서 사용한 htmlspecialchars()를 함수로 감싸 보자.

<?php
function e(string $value): string
{
    return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

$post = [
    'title' => '<script>alert(1)</script>',
    'author' => '지민',
];
?>

<h2><?= e($post['title']) ?></h2>
<p>작성자: <?= e($post['author']) ?></p>

e()라는 짧은 이름은 실무에서도 자주 쓰이는 방식이다. 출력할 때마다 긴 함수를 반복해서 쓰지 않아도 되고, 안전한 출력 습관을 유지하기 쉽다. ENT_QUOTES는 작은따옴표와 큰따옴표까지 변환하도록 하고, ENT_SUBSTITUTE는 잘못된 문자 인코딩이 섞였을 때 빈 문자열로 사라지는 대신 대체 문자로 처리하도록 도와준다.

단, 이 함수가 모든 보안을 해결해 주는 것은 아니다. HTML 본문에 출력할 때 도움이 되는 함수라고 이해하자. URL, SQL, JavaScript 안에 값을 넣을 때는 상황에 맞는 다른 처리가 필요하다.

require와 include로 파일 나누기

코드가 길어지면 한 파일에 모든 함수를 넣기 어렵다. PHP는 다른 파일을 불러와 코드를 나누어 관리할 수 있다.

자주 쓰는 함수들을 functions.php 파일에 모아 보자.

<?php
// functions.php
function e(string $value): string
{
    return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

function formatWon(int $price): string
{
    return number_format($price) . '원';
}

그리고 index.php에서 불러온다.

<?php
require __DIR__ . '/functions.php';

$product = [
    'name' => '무선 마우스',
    'price' => 29000,
];
?>

<h2><?= e($product['name']) ?></h2>
<p><?= formatWon($product['price']) ?></p>

require는 파일을 반드시 불러와야 할 때 사용한다. 파일이 없으면 오류가 발생하고 실행이 중단된다. include도 파일을 불러오지만, 실패해도 경고를 내고 실행을 계속하려고 한다.

일반적으로 함수 파일, 설정 파일처럼 없으면 프로그램이 제대로 동작할 수 없는 파일은 require를 사용한다.

또한 같은 파일을 여러 번 불러오는 실수를 막고 싶다면 require_once를 사용할 수 있다.

<?php
require_once __DIR__ . '/functions.php';

__DIR__은 현재 PHP 파일이 있는 디렉터리 경로를 뜻한다. 상대 경로 문제를 줄이기 위해 파일을 불러올 때 함께 쓰면 좋다.

작은 기능으로 묶어 보기

게시글 목록을 출력하는 작은 예제로 함수를 활용해 보자.

<?php
require_once __DIR__ . '/functions.php';

function createExcerpt(string $text, int $length = 30): string
{
    if (mb_strlen($text) <= $length) {
        return $text;
    }

    return mb_substr($text, 0, $length) . '...';
}

$posts = [
    ['title' => 'PHP 함수 기초', 'body' => '함수는 반복되는 코드를 이름 붙여 재사용하게 해 줍니다.'],
    ['title' => '스코프 이해하기', 'body' => '변수는 사용할 수 있는 범위가 정해져 있습니다.'],
];
?>

<ul>
    <?php foreach ($posts as $post): ?>
        <li>
            <strong><?= e($post['title']) ?></strong><br>
            <?= e(createExcerpt($post['body'], 20)) ?>
        </li>
    <?php endforeach; ?>
</ul>

이 예제에서 e()는 안전한 출력을 담당한다. createExcerpt()는 본문 줄이기를 담당한다. foreach는 목록 출력을 담당한다. 각 코드의 역할이 분리되어 있어 읽기 쉽다.

함수를 잘 만든다는 것은 어려운 기술을 많이 쓰는 것이 아니다. 한 함수가 한 가지 일을 하게 만드는 것이다.

실습 과제

  1. functions.php 파일을 만들고 다음 함수를 작성해 보자.

    • e(string $value): string

    • formatWon(int $price): string

    • discount(int $price, int $rate): int

  2. 상품 배열을 만들고, 할인된 가격을 화면에 출력해 보자.

<?php
$products = [
    ['name' => '노트북 거치대', 'price' => 32000, 'discount_rate' => 10],
    ['name' => 'USB 허브', 'price' => 18000, 'discount_rate' => 5],
];
  1. 상품명은 반드시 e()로 출력한다.

  2. 가격은 formatWon()으로 출력한다.

  3. 할인 계산은 discount() 함수 안에서 처리한다.

가능하다면 계산 함수 안에서는 echo를 사용하지 말고, return으로 결과만 돌려주자.

자주 발생하는 오류 점검

  • 함수 이름을 잘못 입력했다.

    • formatWon()으로 선언하고 formatWom()처럼 오타를 내지 않았는지 확인한다. PHP의 일반 함수 이름은 대소문자를 구분하지 않는 편이지만, formatwon()처럼 섞어 쓰면 코드를 읽기 어려워진다. 선언한 표기 그대로 일관되게 호출하는 습관을 들이자.

  • 매개변수 개수가 맞지 않는다.

    • discount($price, $rate)처럼 두 개를 받아야 하는 함수에 하나만 넘기지 않았는지 확인한다.

  • 반환값을 출력하지 않았다.

    • formatWon(10000);만 쓰면 결과를 사용하지 않는다. 화면에 보이게 하려면 echo formatWon(10000);처럼 작성한다.

  • 함수 안에서 바깥 변수를 바로 사용하려고 했다.

    • 필요한 값은 매개변수로 넘긴다. global은 특별한 이유가 있을 때만 사용한다.

  • 같은 함수를 두 번 선언했다.

    • 같은 파일을 여러 번 불러오면 함수 중복 선언 오류가 날 수 있다. 공통 함수 파일은 require_once로 불러오자.

  • 파일 경로가 틀렸다.

    • require 'functions.php';가 실패한다면 현재 실행 중인 파일의 위치를 확인한다. 안전하게 require_once __DIR__ . '/functions.php'; 형태를 사용하자.

  • mb_strlen() 또는 mb_substr() 함수를 찾을 수 없다는 오류가 난다.

    • 한글 문자열을 안전하게 자르기 위해 사용하는 mbstring 확장이 꺼져 있을 수 있다. 로컬 개발 환경의 PHP 설정에서 mbstring이 활성화되어 있는지 확인하자.

함수는 코드를 짧게 만들기 위한 문법만이 아니다. 프로그램을 역할별로 나누는 방법이다. 이제부터 작성하는 PHP 코드는 한 파일에 길게 늘어놓기보다, 의미 있는 함수로 나누어 읽기 쉬운 구조를 만드는 방향으로 발전시켜야 한다.

책 목록

PHP 교과서: 기초 문법부터 게시판 프로젝트와 배포까지

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기