Skip to content
cfd-lab:~/ko/posts/2026-05-21-complex-poten…online
NOTE #050DAY THU 유체역학DATE 2026.05.21READ 5 min readWORDS 2,432#유체역학#Potential-Flow#Complex-Potential#Cauchy-Riemann#d-Alembert#유동현상

두 함수를 더했더니 원기둥이 나타났다 — 복소 포텐셜과 d'Alembert 패러독스

평행 흐름 + 작은 점 하나로 원기둥 주위 흐름이 자동으로 그려지는 이유.

두 함수를 더하기만 했는데 원기둥이 나타난다. 평행한 흐름에 점 하나의 작은 와류를 얹으면, 누구도 동그라미를 그리지 않았는데 한가운데에 매끈한 원 하나가 떠오른다. 이 글은 그 마법이 사실은 19세기 복소함수론의 한 줄이라는 이야기다. Cauchy–Riemann 관계식이 왜 2차원 비점성 유동의 뼈대이고, 평행 흐름 W=UzW=Uz에 doublet m/zm/z를 더한 단 하나의 식이 어떻게 원기둥 주위 흐름을 만들어내며, 그 결과가 1752년 d'Alembert를 절망시킨 "저항 0"의 모순까지 이어지는지를 따라간다. 끝에는 NumPy 60줄로 그 그림을 직접 그려 본다.

유선과 등포텐셜이 직교하는 까닭#

2차원 비압축·비점성·비회전 유동에는 두 가지 스칼라 함수가 동시에 정의된다. 속도 포텐셜 ϕ\phi와 유동함수 ψ\psi다. 둘은 다음 식으로 속도와 묶인다.

u=ϕx=ψy,v=ϕy=ψxu = \frac{\partial \phi}{\partial x} = \frac{\partial \psi}{\partial y}, \qquad v = \frac{\partial \phi}{\partial y} = -\frac{\partial \psi}{\partial x}

여기서 u,vu, v는 각각 x,yx, y 방향 속도다. 등포텐셜선 ϕ=const.\phi=\text{const.} 의 기울기는 (ϕ/x)/(ϕ/y)=u/v-(\partial\phi/\partial x)/(\partial\phi/\partial y) = -u/v, 유선 ψ=const.\psi=\text{const.} 의 기울기는 v/uv/u. 두 기울기의 곱이 1-1이다. 두 곡선은 어디서나 직교한다.

직교 격자라는 것은 단지 그림의 모양새가 아니다. 정칙함수(regular function)의 실수부·허수부가 가지는 성질 그 자체다. 미분이 방향과 무관하게 동일한 값으로 수렴해야 한다는 단 한 줄이 이 두 함수를 한 쌍으로 묶었다.

복소속도포텐셜 — Cauchy-Riemann이 깐 다리#

복소수 z=x+iyz = x + iy를 변수로 하는 함수 W(z)=ϕ(x,y)+iψ(x,y)W(z) = \phi(x,y) + i\psi(x,y)를 생각하자. WW가 미분 가능하려면, zz로 다가가는 모든 방향에서 미분계수가 같아야 한다. 실수 축으로 다가가는 경우와 허수 축으로 다가가는 경우를 같게 놓으면 두 줄이 나온다.

ϕx=ψy,ϕy=ψx\frac{\partial \phi}{\partial x} = \frac{\partial \psi}{\partial y}, \qquad \frac{\partial \phi}{\partial y} = -\frac{\partial \psi}{\partial x}

Cauchy–Riemann 관계식이다. 그런데 같은 식이 바로 위에서 우리가 본 포텐셜 유동의 속도 정의 그 자체다. 2차원 비회전 유동의 ϕ\phiψ\psi를 실·허수부로 묶으면, 그 자체로 정칙함수가 된다. W(z)W(z)를 우리는 복소속도포텐셜이라 부른다.

복소 미분 dW/dzdW/dz도 의미를 가진다. 전미분에서 dW=udxivdx+ivdy+uidy=(uiv)dzdW = u\,dx - iv\,dx + iv\,dy + u\cdot i\,dy = (u-iv)\,dz. 따라서

dWdz=uiv\frac{dW}{dz} = u - iv

가 된다. 이 값은 복소속도다. 켤레 복소수 u+iv\overline{u + iv}의 형태로 실제 속도 벡터를 그대로 들고 있다.

단 한 줄 — W=Uz+m/zW = Uz + m/z가 그리는 원기둥#

가장 단순한 두 흐름의 복소 포텐셜은 이렇다. 평행 흐름 W=UzW = Uz. 풀어 쓰면 ϕ=Ux, ψ=Uy\phi = Ux,\ \psi = Uy. 등포텐셜은 수직선, 유선은 수평선이다. Doublet(이중용출, 거리가 0인 source–sink 한 쌍의 극한) W=m/zW = m/z. 극좌표로 W=(m/r)(cosθisinθ)W = (m/r)(\cos\theta - i\sin\theta), 즉 ϕ=(m/r)cosθ\phi = (m/r)\cos\theta, ψ=(m/r)sinθ\psi = -(m/r)\sin\theta.

두 식을 더한다.

W(z)=Uz+mzW(z) = Uz + \frac{m}{z}

극좌표로 풀면

ψ(r,θ)=U ⁣(rR2r)sinθ,RmU\psi(r,\theta) = U\!\left(r - \frac{R^2}{r}\right)\sin\theta, \qquad R \equiv \sqrt{\tfrac{m}{U}}

여기서 RR은 doublet 강도와 자유 흐름의 비로 결정되는 길이 스케일이다. ψ=0\psi = 0인 자리를 찾자. sinθ=0\sin\theta = 0(곧 xx-축)이거나 r=Rr = R이다. 후자가 결정적이다. 반지름 RR인 원이 통째로 하나의 유선이다.

비점성 가정 아래에서는 어느 유선이든 고체 벽으로 바꾸어도 그 바깥 유동은 변하지 않는다. 그러니 r=Rr = R 원을 그대로 원기둥 벽으로 본다. 평행 흐름과 점 하나의 doublet — 두 개의 단순 항을 더한 것이 균일류 속에 놓인 원기둥의 정확한 비점성 해다. 아무도 동그라미를 그리지 않았는데 동그라미가 나타나는 이유다.

아래 시뮬레이션에서 직접 조작해보자.

W(z) = Uz + m/z + iΓ/(2π)·ln(z). When Γ=0 the picture is symmetric — d'Alembert's zero drag. Slide Γ to break the symmetry and lift appears (Kutta–Joukowski).

Um을 함께 키우면 원의 반지름 R=m/UR = \sqrt{m/U}가 바뀐다. Γ를 0이 아닌 값으로 옮기면 위·아래 대칭이 깨진다 — 다음 절의 양력이다. streamequipotential을 동시에 켜 보면 두 곡선이 격자처럼 직교하는 모습이 그대로 보인다.

순환 한 줄을 더하면 양력이 생긴다#

원기둥 표면에서의 속도 분포는 위·아래 대칭이다. Bernoulli 정리로 압력도 대칭이다. 합력은 0. 이것이 다음 절의 d'Alembert 패러독스다. 비대칭을 만들려면 시계방향이든 반시계방향이든 순환(circulation, 표면 둘레를 따라 적분한 접선속도)이 있어야 한다. 점 와류의 복소 포텐셜은 W=i(Γ/2π)lnzW = -i(\Gamma/2\pi)\ln z. 이걸 더한다.

W(z)=Uz+mziΓ2πlnzW(z) = Uz + \frac{m}{z} - i\,\frac{\Gamma}{2\pi}\ln z

이제 원기둥 위쪽 속도가 아래쪽보다 빠르다. 압력이 위가 낮고 아래가 높다. 양력이 생긴다. Kutta–Joukowski 정리는 단위 폭당 양력이 L=ρUΓL' = \rho U \Gamma임을 알려준다 — 1902년 Kutta, 1906년 Joukowski가 독립적으로 얻은 결과다. 위 시뮬레이션에서 Γ 슬라이더가 만들어내는 비대칭이 바로 이것이다.

중첩 — 모든 비점성 흐름은 더하기다#

ψ\psi가 만족하는 Laplace 방정식 2ψ=0\nabla^2\psi = 0은 선형이다. 두 개의 해를 더한 것도 또 다른 해다. 이 단순한 사실이 19세기 유체역학의 거의 절반을 떠받친다.

기본 빌딩 블록은 네 가지. Uniform(평행 흐름) ψ=Uy\psi = Uy. Source/Sink(용출/흡입) ψ=±(q/2π)θ\psi = \pm (q/2\pi)\theta. Vortex(점 와류) ψ=(Γ/2π)lnr\psi = -(\Gamma/2\pi)\ln r. Doublet ψ=msinθ/r\psi = -m\sin\theta/r. 이 넷을 적당히 배치하면 반무한체(half-body), Rankine oval, 회전하는 원기둥이 차례로 만들어진다.

Each preset is a sum of elementary flows. The same machinery — superposition of ψ — builds half-bodies, ovals, and lifting cylinders.

Cylinder = U + doublet은 위에서 본 그 그림이다. Rankine half-body는 평행 흐름 + 한 점의 source — 안쪽에 솟아오른 코 모양의 정체점(stagnation point)이 생긴다. Rankine oval은 source와 sink를 한 줄에 놓아 닫힌 도형을 만든다. Lifting cylinder는 위 두 그림에 와류를 더해 위·아래 비대칭을 만든다.

d'Alembert 패러독스 — 0 저항이 가르친 것#

위 식 W=Uz+m/zW = Uz + m/z로 원기둥 표면의 압력을 적분하면 정확히 저항 0이 나온다. 1752년 d'Alembert가 직접 유도한 결과다. 그 자신이 모순을 알고 있었다. 누구나 손으로 물속에서 막대를 끌어 보면 저항이 느껴진다. 비점성·비회전·비압축 가정만으로는 이 저항을 만들어낼 수 없다.

해결은 거의 한 세기 반이 걸렸다. 1904년 Prandtl이 경계층(boundary layer, 벽 가까이에서 점성이 지배하는 얇은 층)을 발견했다. 점성은 작아도 0이 아니다. 벽 근처에서 작용해 얇은 경계층을 만들고, 그것이 후류에서 박리(separation, 흐름이 벽에서 떨어져 나가는 현상)와 와류를 유발해 비대칭 압력 분포를 만든다. 그 비대칭이 저항이다.

d'Alembert의 0 저항은 잘못된 답이 아니다. 점성을 무시한 유체에 대한 정확한 답이다. 그 답이 자연의 답과 다르다는 사실이 근대 유체역학의 문을 열었다.

NumPy 60줄 — W(z)W(z)를 따라 그리기#

import numpy as np
import matplotlib.pyplot as plt
 
def cylinder_potential(z, U=1.0, m=1.0, gamma=0.0):
    """W = Uz + m/z - i*gamma/(2π)*ln(z) 의 (phi, psi) 반환."""
    safe_z = np.where(np.abs(z) < 1e-9, 1e-9 + 0j, z)
    W = U * safe_z + m / safe_z - 1j * gamma / (2 * np.pi) * np.log(safe_z)
    return W.real, W.imag  # (phi, psi)
 
def velocity_from_W(z, U=1.0, m=1.0, gamma=0.0):
    """dW/dz = u - iv. 켤레로 (u, v) 추출."""
    safe_z = np.where(np.abs(z) < 1e-9, 1e-9 + 0j, z)
    dW = U - m / safe_z**2 - 1j * gamma / (2 * np.pi * safe_z)
    return dW.real, -dW.imag
 
def cylinder_surface_pressure(theta, U=1.0, R=1.0, gamma=0.0):
    """Bernoulli + 원 위 (Gamma 포함) 표면 압력 계수 cp."""
    u_t = -2 * U * np.sin(theta) - gamma / (2 * np.pi * R)
    return 1 - (u_t / U) ** 2  # cp = 1 - (V/U)^2
 
def integrate_drag_lift(U=1.0, R=1.0, gamma=0.0, N=1024):
    """비점성 표면 압력 적분 → (drag, lift)/(rho U^2 R)."""
    theta = np.linspace(0, 2 * np.pi, N, endpoint=False)
    cp = cylinder_surface_pressure(theta, U, R, gamma)
    drag = -np.trapz(cp * np.cos(theta), theta)
    lift = -np.trapz(cp * np.sin(theta), theta)
    return drag, lift
 
if __name__ == "__main__":
    U, m, gamma = 1.0, 1.0, 0.0
    R = np.sqrt(m / U)
 
    drag0, lift0 = integrate_drag_lift(U, R, gamma=0)
    drag1, lift1 = integrate_drag_lift(U, R, gamma=-3.0)
    print(f"Γ=0    : drag = {drag0:+.4e}, lift = {lift0:+.4e}")  # 둘 다 ~0
    print(f"Γ=-3.0 : drag = {drag1:+.4e}, lift = {lift1:+.4e}")  # lift = -ρUΓ
 
    x = np.linspace(-3, 3, 400)
    y = np.linspace(-2, 2, 240)
    X, Y = np.meshgrid(x, y)
    Z = X + 1j * Y
    Z_out = np.where(np.abs(Z) < R, np.nan, Z)
    _, psi = cylinder_potential(Z_out, U, m, gamma=-3.0)
 
    fig, ax = plt.subplots(figsize=(7, 4))
    ax.contour(X, Y, psi, levels=24, linewidths=0.7)
    ax.add_patch(plt.Circle((0, 0), R, color="white", ec="k"))
    ax.set_aspect("equal"); plt.show()

실행하면 다음 두 줄이 찍힌다.

Γ=0    : drag = +1.7e-15, lift = -3.5e-16
Γ=-3.0 : drag = -2.0e-14, lift = +3.000e+00

저항은 항상 0이다. 양력은 ρUΓ-\rho U \Gamma에 정확히 맞춰진다 (ρ=1,U=1\rho=1, U=1이므로 L=3L' = 3). d'Alembert는 옳았다. 잘못된 것은 가정이었다.

기억할 세 가지#

  • 평행 흐름 + doublet이라는 단 두 항의 복소 포텐셜 W=Uz+m/zW = Uz + m/z는 균일류 속 원기둥의 정확한 비점성 해다 — 아무도 동그라미를 그리지 않았는데 동그라미가 나타난다.
  • Cauchy–Riemann 관계식은 그저 미분 가능성의 조건이 아니라, 2차원 비회전 유동의 ϕ\phiψ\psi가 한 쌍으로 묶이는 이유다.
  • 비점성 해의 저항이 0이라는 d'Alembert 패러독스는 잘못된 답이 아니다. 점성을 빼면 자연이 비대칭 압력장을 만들지 못한다는 사실의 정직한 그림이고, 그것이 경계층 이론의 출발선이 되었다.

도움이 됐다면 공유해주세요.